Insecure data transfer
Data transferred unencrypted can be sniffed. This can not only give an attacker valuable information, but also the content of session cookies, allowing him to hijack a session. Additionally, non-secure communication can be modified by an attacker.
To prevent this type of attack
- Use SSL/TLS (https) for any and all data transfer
- Do not start communicating via http, only redirecting to https when “needed”
- Mark cookies with the “secure” attribute
- Use the Strict-Transport-Security header where possible
- Educate users to visit the https:// URL directly
- If your web application performs HTTPS requests, make sure it verifies the certificate and host name
- Consider limiting trusted CAs if connecting to internal servers
Using https ensures all data transfer is encrypted and the server is authenticated. Redirects sent on unencrypted pages can be removed or modified by the attacker. Thus, the transition from plain http to https can be sabotaged, making any plain http communication before switching to https dangerous. Marking the cookies secure-only ensures they are never transferred via unencrypted connections to prevent sniffing.
The STS header ensures that after the first visit, even if users visit the http:// URL, the request is performed via secure https. This prevents attacks like the SSLstrip attack on the unencrypted redirect. Educating the user to visit the https:// URL directly provides this protection for the first request and browsers that do not support STS and thus ignore the header. This education can be supported by serving nothing or only an information page without a clickable link on port 80 to force users to enter the correct URL and remove the incentive to be lazy and omit the “https://”.
In some web applications, the web server performs HTTPS requests (for example when fetching or pushing data to APIs or running the OpenID or OAuth protocols). HTTPS is only secure if the software initiating the connection (i.e. your web application) correctly verifies the remote certificate:
- Checks if the certificate is still valid
- Checks if the certificate is signed by a trusted CA (a list of trusted CAs is needed)
- Checks if the hostname you are connecting to matches the name in the certificate (the wrapper performing the SSL handling needs access to the host name)
Some libraries do not do this by default, making HTTPS connections insecure! Consider it suspicious if you are not required to provide a list of trusted CAs, or it looks like the SSL wrapper does not have access to the host name you are connecting to. To test for this isssue, attempt to connect to a host that uses a non-expired selfsigned certificate, then attempt to connect to a host that uses a valid certificate, but use a different hostname (e.g. address the host by its IP address) than the one specified in the certificate. If either of these connections succeed, your library/configuration is insecure.
In PHP, both standard ways to perform HTTP(S) requests have issues: The cURL library doesn't check certificates by default if used with cURL below version 7.10. The Stream API always requires explicit configuration (affecting all functions using
file_get_contents()). For cURL, set
CURLOPT_CAINFO. For the Stream API, use a stream context with the
cafile SSL context options.
If you are connecting to internal servers, consider limiting the list of trusted CAs to the CA you are using. This reduces the risk from compromised/malicious CAs. The default CA bundles often include CAs which you may not consider trustworthy, e.g. the Chinese internet authority CNNIC.