Non-encrypted confidential information
Need
Secure storage, transmission, and encryption of confidential information and credentials
Context
• Usage of Go 1.16 for building high-performance and scalable applications
• Usage of Go 1.13 for building high-performance and scalable applications
• Usage of Go 1.15 for building high-performance and scalable applications
• Usage of Gin for building web applications in Go
• Usage of gin-gonic/gin for building web applications in Go
• Usage of io/ioutil for reading and writing files in Node.js
• Usage of net/http for building HTTP servers in a Node.js application
• Usage of gorm for Object-Relational Mapping (ORM) in Go programming
• Usage of GORM SQLite dialect for database operations
• Usage of gopkg.in/ldap.v2 for LDAP (Lightweight Directory Access Protocol) integration
Description
1. Non compliant code
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"gopkg.in/ldap.v2"...**Plain-text credential storage, hardcoding, and transmission.** A Gin server's `/login` endpoint stores the submitted username and password directly in a file called `credentials.txt` in plain text, and the `/admin` endpoint hardcodes a username and password into the source code, returning them as a JSON response when accessed. The credentials are stored, hardcoded, and transmitted without any encryption or hashing, so anyone who gains access to `credentials.txt`, the source code, or the `/admin` response can read them without any additional effort. **Credit card details sent and processed without encryption.** The `/card-info` endpoint binds a POST body to a `CardInfo` struct (card number, CVV, expiry date) and processes it without ever encrypting the fields, in violation of the PCI DSS standard, which requires cardholder data to be encrypted at capture, in transit, and at rest. An attacker who intercepts the traffic can see the credit card information in plain text. **Plain-text password stored in the database.** The `/users` endpoint uses GORM with SQLite to create a `User` record from the submitted `name`, `email`, and `password`, storing the password directly with no encryption or hashing. Anyone who gains access to the database can read every user's password. **LDAP service credentials hardcoded in plain text.** The `/ldap` endpoint dials an LDAP server and calls `l.Bind("cn=read-only-admin,dc=example,dc=com", "password")`, hardcoding the bind DN and password directly in the source code. Anyone with access to the source code can see these credentials, and rotating them requires changing and redeploying the code. **Reversible Base64 encoding used in place of encryption.** The `/secret` endpoint checks HTTP Basic Auth credentials against literals, while a package-level `credentials` variable stores the Base64 encoding of `"Aladdin:OpenSesame"` directly in the source code. Base64 is only an encoding, not encryption — it does not provide any real confidentiality, so anyone with the source code can trivially decode it. **Hexadecimal-encoded data returned without encryption.** The `/confidential` endpoint returns a hexadecimal string (`"48656c6c6f2c20576f726c64"`, i.e. `"Hello, World"`) as the response body. Hexadecimal is only an encoding, not encryption, so anyone who intercepts the response can decode it back to plain text with any online hex-to-text converter.
2. Steps
• Use a secure method to store confidential information, such as passwords, and avoid storing or transmitting it in plain text.
• Do not hardcode credentials in the source code; use environment variables or a secure secrets manager instead.
• Encrypt or hash the confidential information before storing or transmitting it.
• Choose a strong encryption algorithm and generate a secure encryption key.
• Ensure that encryption keys and credentials are securely stored and managed.
• Use a secure storage mechanism, such as a database, to store the confidential information.
• Implement secure authentication and authorization mechanisms (e.g., OAuth or JWT instead of Basic Authentication) to protect the confidential information.
• Implement proper access control mechanisms to restrict access to sensitive endpoints and encrypted information.
• Implement Transport Layer Security (TLS) or another secure communication protocol (e.g., HTTPS) to encrypt communication between the client and the server.
• Implement input validation and sanitization to prevent SQL injection attacks.
• Consider implementing additional security measures, such as two-factor authentication, to enhance the protection of the confidential information.
• Follow the PCI DSS standard guidelines for handling and protecting credit card information.
• Regularly update and patch the software to address any security vulnerabilities.
• Perform regular security audits and penetration testing to identify and address any vulnerabilities.
• Regularly review and update the security measures in place to protect the confidential information.
3. Secure code example
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"encoding/hex"...**`/login`/`/admin`.** The password is now hashed with `bcrypt.GenerateFromPassword` before being written to `credentials.txt`, and the `/admin` credentials are read from the `ADMIN_USERNAME`/`ADMIN_PASSWORD` environment variables (with the password hashed before being returned) instead of being hardcoded. A production deployment should store the hashed values in a database rather than a flat file, use a proper secrets manager instead of plain environment variables, and serve everything over HTTPS. **`/card-info`.** The card number, CVV, and expiry date are now encrypted with AES-GCM via the `encrypt` helper before being processed, and the server runs with `RunTLS` so all communication with the client is encrypted in transit. The AES key shown here is a placeholder and should be generated and stored securely, as should the TLS certificate/key pair. **`/users`.** The password is now hashed with `bcrypt.GenerateFromPassword` before the `User` record is created, so even if an attacker gains access to the database they cannot recover the original password. The `Password` column is widened accordingly (`gorm:"type:varchar(100);"`). **`/ldap`.** The LDAP host, bind DN, and password are now read from the `LDAP_HOST`, `LDAP_BIND_DN`, and `LDAP_PASSWORD` environment variables instead of being hardcoded, so they are not exposed in the source code. **`/secret`.** The Basic Auth credentials are now compared against the `USERNAME`/`PASSWORD` environment variables instead of a Base64-encoded literal baked into the source code. **`/confidential`.** The confidential value is now encrypted with AES-CFB via the `encryptHex` helper before being hex-encoded and returned, instead of being sent as a bare hexadecimal encoding of the plain text. The key used here is a placeholder and should be securely generated and stored in production.
References
• 020. Non-encrypted confidential information