لیستم موارد امنیتی برنامه وب
Web Application Security Checklist
Data Encryption & Protection
Encrypt all data in transit:
All customer data transmitted over networks must use TLS 1.2 or higher. Unencrypted protocols like HTTP or FTP are strictly prohibited. This prevents unauthorized interception during transfers.
Encrypt all data at rest:
Customer data stored in databases, backups, or cloud systems must be encrypted using AES-256. Encryption keys must be managed through certified services (e.g., AWS KMS, Azure Key Vault) and stored separately from encrypted data. This ensures protection even if physical storage is compromised.
Tokenize sensitive fields:
Replace direct identifiers (e.g., emails, phone numbers) with tokens in non-production environments. Tokenization minimizes exposure if test systems are breached.
Do not store PII without legal enforcement:
Personally Identifiable Information (PII) must not be stored unless explicitly required by law or regulation. If stored, legal justification must be documented and approved in writing. This reduces liability and ensures compliance with privacy laws.
Access Control & Authentication
Enforce Multi-Factor Authentication (MFA):
MFA (e.g., authenticator apps, hardware tokens) is mandatory for all accounts accessing customer data. This reduces risks from stolen passwords or compromised credentials.
Assign role-based access permissions:
Grant access strictly based on job roles (e.g., "read-only" for analysts, "admin" for IT teams). Conduct quarterly reviews to revoke unused accounts. Limiting access prevents accidental or intentional misuse.
Automatically terminate inactive sessions:
Sessions must timeout after 15 minutes of inactivity. Accounts unused for 6 months will be permanently disabled unless extended retention is legally required. This reduces exposure from unattended devices while accommodating legal obligations.
Data Handling & Storage
Delete data after retention periods:
Securely delete customer data within 6 months of contract termination using irreversible overwrite methods (e.g., NIST 800-88 standards). If legal enforcement requires extended retention, written approval and a data protection plan must be provided. This balances compliance with liability reduction.
Store data only in approved environments:
Use pre-approved encrypted platforms (e.g., AWS S3, Azure Blob Storage). Storage on personal devices, unauthorized cloud services, or unsecured physical media is prohibited. This ensures data remains in controlled, auditable environments.
Mask sensitive data in logs/UIs:
Partially obscure sensitive information in internal systems (e.g., display only the last 4 digits of credit cards, replace full names with initials). This protects data even during routine operations or internal reviews.
Network Security
Isolate systems with firewalls/segmentation:
Separate networks handling customer data using firewalls, VLANs, or software-defined segmentation. This limits attackers’ ability to move laterally during breaches.
Secure APIs with authentication and rate limits:
Authenticate API calls using OAuth 2.0 or API keys. Enforce rate-limiting and monitor for abnormal usage patterns (e.g., sudden spikes in requests). This prevents unauthorized scraping or brute-force attacks.
Use a WAF
Put a web application firewall product in front of your application. This will make many vulnerabilities significantly harder to find and exploit. ModSecurity is a good open-source option.
Further reading
Configure your web server carefully to avoid HTTP desync attacks
There is an attack called "HTTP Desync" or "Request Smuggling", which could allow for an attacker to do all sorts of nasty things, such as steal HTTP requests of random users collecting to the web application, if the following conditions are true:
- There is a frontend web server, such as a load balancer/any reverse proxy, that accepts requests with both, Content-Length and Transfer-Encoding headers, and passes them on without normalizing the request.
- The next web server on the line, such as an application webserver, uses, or can be tricked to use, a different mechanism than the frontend webserver to determine where the HTTP request begins and where it ends, e.g. the frontend would use Content-Length whereas the application server would use Transfer-Encoding.
- The front-end web server reuses the connection to the backend web server.
- The frontend web server uses HTTP/1 (instead of HTTP/2) in the backend server connection.
So how to protect yourself? Depends on the product but in general:
- Consult the documentation/vendor of the e.g. reverse proxy products that you are using and ensure that they are actively defending against the attack.
- Configure the front-end webserver to use HTTP/2 in backend connections.
- Configure the front-end webserver to prevent aggregation of HTTP requests from separate client-side TCP streams into the same server-side connection.
- Use a WAF (Web Application Firewall) and ensure it has a module for thwarting request smuggling attempts
Further reading
Use containers
Run your application in isolation so that in the event of a breach, the attacker will not have unnecessary access to unwanted file-, system-, or network resources. So preferably use something like Kubernetes or a serverless cloud stack for deploying your application. If you are for any reason forced to use a bare server, then manually run e.g. Docker to constrain the application.
Further reading
Contractual Obligations
Adhere to Data Processing Agreements (DPAs):
Use customer data only for agreed purposes. Marketing, reselling, or sharing data with subcontractors requires explicit written consent. This ensures compliance with privacy laws (e.g., GDPR, CCPA) and maintains trust.
Report breaches within 24 hours:
Notify us immediately of any suspected breach, including details of affected data and steps taken for containment to us. Collaborate on forensic investigations and customer notifications. Prompt reporting minimizes reputational and legal risks.
Submit to annual penetration tests:
Conduct third-party penetration tests annually and share results with us. Critical vulnerabilities must be remediated within 30 days. This validates your systems’ resilience against real-world attacks.
Continuous Security Practices
Conduct continuous vulnerability scanning:
Use industry-standard tools to identify unpatched software, misconfigurations, or exposed services. Critical vulnerabilities (CVSS score ≥7.0) must be remediated within 14 days. Proactive scanning reduces exploit risks.
Monitor systems in real time:
Deploy Security Information and Event Management (SIEM) tools to flag suspicious activity (e.g., repeated failed logins, unauthorized access attempts). Retain logs for at least 12 months for forensic purposes. Rapid detection enables swift incident response.
Update security practices annually:
Revise policies, tools, and training programs to address emerging threats (e.g., AI-driven phishing, zero-day exploits). Document changes and share summaries with us upon request. Cybersecurity requires ongoing adaptation to evolving risks.
Defending Web Application Threats
There are a couple of threats on the end user's side that you as a developer can help mitigate. They include:
- Attacks through malicious websites/links in the user's browser.
- Attacks on the user's local network.
- Attacks where someone accesses a shared device before or after the user. For instance, if user data remains stored in the browser cache, other computer users could retrieve it later on.
Let's start our checklist with countermeasures for these threats.
Use HTTPS and only HTTPS to protect your users from network attacks
This one you probably already knew. Encrypt all connections between your user's web browser and your web server. It doesn't hurt also to disable some of the older cipher suites and protocols.
It is not enough to encrypt the "sensitive" portions of a website. An attacker can intercept a single unencrypted HTTP request and then forge a response from the server with malicious content in it.
HTTPS, which stands for Hypertext Transfer Protocol Secure, is a more secure extension of the standard HTTP protocol. HTTPS establishes an encrypted connection between a web server and a client's browser using Transport Layer Security (TLS) or its predecessor, Secure Sockets Layer (SSL).
By using the HTTPS protocol, you can ensure that your applications are being accessed securely. Part of this is done by restricting access to your application through HTTP and only allowing access through HTTPS. If there is an issue with an HTTPS connection, many browsers will let the user know that the site may not be secure, which helps to inform users to be cautious or even avoid the site until the security issue is fixed.
By implementing HTTPS and proper certificate management, you can protect data in transit from man-in-the-middle attacks and interceptions. These types of attacks are easily executed over unsecure connections and networks and can be limited by using HTTPS. Many hosting solutions make it easy to deploy and maintain your applications with secure connections using the principles mentioned above.
Luckily HTTPS is effortless these days. You can get both the certificate (LetsEncrypt) and automatic certificate creation/management (CertBot) free of charge.
Further reading
Use HSTS and preloading to protect your users from SSL stripping attacks
HSTS or Strict-Transport-Security is a header that your server can use to enforce encrypted connections. It says, from here on, always use an encrypted connection (HTTPS) connecting to my domain.
HSTS will prevent so-called SSL stripping attacks where an attacker on the network intercepts the very first HTTP request made by a browser (which is often unencrypted) and forges a reply to that unencrypted HTTP request right away, pretending to be the server and downgrading the connection to intercepted plaintext HTTP from then on.
One caveat is that HSTS will only protect an application if the user has already successfully visited it before. To overcome this limitation, you should submit your site to [https://hstspreload.org\] (https://hstspreload.org) so browser vendors can hardcode your domain to the HSTS list.
Example
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Warning
Be mindful when implementing HSTS. It will force encrypted traffic to your website, and if you still have plain text, your website could break. So start with a small max-age and ramp it up once you're confident that everything still functions properly. And leave preloading as the last step because it's painful to cancel.
Further reading
Serve cookies with the 'Secure' attribute to protect your user from network attacks
Configure your cookies with the Secure attribute. This attribute will prevent them from being leaked over an (accidental or forced) unencrypted connection.
Set-Cookie: foo=bar; ...other options... Secure
Further reading
Generate HTML safely to avoid XSS vulnerabilities
To avoid XSS (Cross-Site Scripting) vulnerabilities, use one of the following:
- Completely static websites (e.g., JavaScript SPA + backend API). The most effective way to avoid problems with generating HTML is not to generate HTML at all. Try NexJS, perhaps. It's pretty cool.
- A template engine. Suppose you have a traditional web application where HTML is generated and parameterized on the backend server. In that case, do not craft HTML through string concatenation. Instead, use a template engine such as Twig for PHP, Thymeleaf for Java, Jinja2 for Python, and so on.
If you use a template engine, ensure it's configured correctly to automatically encode parameters properly, and don't use any "insecure" functions that bypass the automatic encoding. And don't put HTML in dangerous places like event handler code, unquoted attributes, or href/src.
Further reading
Use JavaScript safely to avoid XSS vulnerabilities
To avoid XSS (Cross-Site Scripting) vulnerabilities on the JavaScript side, don't pass any untrusted data into functions or properties that could end up executing code. You have to use common sense here but some of the usual suspects are:
- eval, setTimeout, setInterval, etc.
- innerHTML, React's dangerouslySetInnerHTML, etc.
- onClick, onMouseEnter, onError, etc.
- href, src, etc.
- location, location.href, etc.
Further reading
Sanitize and sandbox untrusted content to avoid XSS and other vulnerabilities
It's best to avoid untrusted content. But sometimes, you have to retrieve raw HTML from, e.g., a remote source and then render it on your website. Or maybe you have to allow your users to write posts with a WYSIWYG editor. There are many use cases.
To avoid XSS (Cross-Site Scripting) vulnerabilities in these scenarios, sanitize the content first with DOMPurify and then render it inside a sandboxed frame.
Even if your WYSIWYG library claims to remove evilness from the HTML, you can break this trust relationship ("I trust my WYSIWYG library to sanitize the content") by re-purifying and sandboxing the content nevertheless. The more trust relationships you break, the more secure your application gets.
There is another common use-case, you want to display e.g. ads on the page. In this case using an IFRAME is not enough because the same-origin-policy for some reason allows cross-origin frames to change the URL of the parent frame (your website) to e.g. a phishing site. Always use the sandbox iframe attribute in these cases to prevent this.
Further reading
Implement an effective Content Security Policy to protect your users from XSS, xsleak and other vulnerabilities
A Content Security Policy (CSP) serves as excellent protection against XSS (Cross-Site Scripting) attacks. It also protects against clickjacking attacks, among other things.
So make sure to use it! CSP, by default, prevents pretty much everything, so the fewer things you put in it, the better. For example, the following is a good policy to start with:
Content-Security-Policy: default-src 'self'; form-action 'self'; object-src 'none'
It allows loading scripts, styles, images, fonts, etc., from the web application's origin but nothing else. Most notably, it will prevent inline scripts (<script>...</script>), which makes exploiting XSS vulnerabilities difficult.
Additionally, the form-action: 'self' directive prevents creating malicious HTML forms on the website (think "Your session has expired, please enter your password here") and submitting them to the attacker's server.
Whatever you do, do not specify script-src: unsafe-inline because then your CSP will lose its mojo.
And finally, if you have concerns about CSP breaking something in production, you can first deploy in Report-Only mode:
Content-Security-Policy-Report-Only: default-src 'self'; form-action 'self'
Further reading
Serve cookies with the HttpOnly attribute to protect them from XSS attacks
Configure your cookies with the HttpOnly attribute. This attribute will prevent them from being accessed by JavaScript code. And this makes them more challenging for an attacker to steal in the event of a successful XSS (Cross-Site Scripting) attack. Of course, you cannot do this for the cookies that need to be accessed by JavaScript.
Set-Cookie: foo=bar; ...other options... HttpOnly
Further reading
Serve downloads with a proper Content-Disposition header to avoid XSS vulnerabilities
To avoid XSS (Cross-Site Scripting) vulnerabilities when serving downloads to your users, send them with a Content-Disposition header that indicates an attachment. This way, the file won't render in the end user's browser directly, resulting in an XSS vulnerability in the case of, e.g., HTML or SVG files.
Content-Disposition: attachment; filename="document.pdf"
Suppose you want some specific files to open in the browser (like perhaps PDF documents for usability reasons), and you know that it's safe to do so. In that case, you can omit the header or change attachment to inline for that particular file extension/extensions.
Further reading
Serve API responses with a proper Content-Disposition header to avoid reflected download vulnerabilities
An attack called reflected file download (RFD) works by crafting a URL that downloads as a malicious file extension from your API, reflecting a malicious payload inside it.
You can prevent this attack by returning a Content-Disposition header with a safe filename in your API HTTP responses.
Content-Disposition: attachment; filename="api.json"
Further reading
Use your platform's anti-CSRF mechanism to avoid CSRF vulnerabilities
To protect against Cross-Site Request Forgery (CSRF) vulnerabilities, ensure that your platform's anti-CSRF mechanism is enabled and working as intended.
Further reading
Validate the OAuth/OIDC state parameter to avoid CSRF vulnerabilities
There is a CSRF attack related to OAuth/OIDC where the attacker unwittingly logs the user in with the attacker's account. If you are using OAuth/OIDC, make sure that your library is validating the state parameter.
Further reading
Use HTTP verbs properly to avoid CSRF vulnerabilities
Never use anything except for POST, PUT, PATCH or DELETE for making any changes. GET requests, for example, are usually not covered by anti-CSRF mechanisms.
Further reading
Serve cookies with the SameSite attribute to protect your users from CSRF vulnerabilities, xsleaks and sometimes XSS as well
Configure your cookies with the SameSite attribute. SameSite will prevent most CSRF (Cross-Site Request Forgery) attacks, where a malicious website submits e.g., a form on behalf of your unwitting user.
There are two modes, Lax and Strict.
The Lax mode is just fine for preventing most cross-site timing and CSRF attacks, except GET-based CSRF vulnerabilities where you make the mistake of making changes (e.g., modifying some database record) in a GET request handler. The Strict mode prevents that sort of blunders from being exploited as well.
However, the Strict mode has another powerful side effect; it makes reflected XSS (Cross-Site Scripting) vulnerabilities practically impossible to exploit as well.
The Strict mode is not well suited for most applications because it breaks authenticated links. If your user is logged in and opens a link on another website to the application, then the tab/window that opens will not be logged in for the user. The session cookie doesn't tag along with the request due to the strict mode.
But at least implement SameSite in Lax mode, there's no harm in doing so, and it serves as an excellent safeguard against CSRF and cross-site timing attacks.
Set-Cookie: foo=bar; ...other options... SameSite=Lax
...or:
Set-Cookie: foo=bar; ...other options... SameSite=Strict
Further reading
Create a fresh session ID on login to protect against session fixation attacks
Next on our checklist is session fixation attacks. Here is how they might work:
- An attacker injects a cookie, say, JSESSIONID=ABC123 into your user's browser. There are many ways the attacker can go about this.
- Your user logs in with their credentials, submitting the attacker's chosen JSESSIONID=ABC123 cookie in the login request.
- Your application authenticates the cookie, and the user is authenticated from that point onwards.
- The attacker who also has the cookie is also logged on as the user from that point onwards.
To prevent this, create a new, authenticated session ID and return it to the user instead of authenticating the existing cookie that might be compromised.
Further reading
Name your cookies right to protect against session fixation attacks
You didn't expect to find cookie naming in an application security checklist, did you? This is not very widely known, but when it comes to cookies, name matters! Name your cookies __Host-Something and web browsers will...
- Not allow for the cookie to be set over an unencrypted connection which protects against session fixation attacks and other threats related to an attacker forcing a cookie into the user's browser.
- Not allow for subdomains to overwrite the cookie, which protects against similar attacks from compromised/malicious subdomains.
Set-Cookie: __Host-foo=bar ...options...
Further reading
Serve proper Cache-Control headers to protect your user's data from subsequent computer users
By default, web browsers cache everything they see to speed up page loads and save network bandwidth.
Caching is a synonym for storing visited websites and downloaded files on disk unencrypted until someone manually deletes them.
The users of your application should be able to trust that once they log out, they are logged out, and they can safely leave the (e.g., library) computer.
For this reason, there is a header called Cache-Control which you should return appropriately in all HTTP responses that contain non-public/non-static content.
Cache-Control: no-store, max-age=0
Further reading Cache-Control
Serve a Clear-Site-Data header upon log out to protect your user's data from subsequent computer users
Another useful header for ensuring that user data gets cleared upon logout is the new Clear-Site-Data header. You can send it in an HTTP response when the user logs out. The browser will clear the cache, cookies, storage, and execution contexts (this is not yet implemented at the time of this writing) for the domain. Most browsers support it; Safari notably still doesn't.
You can send it as follows:
Clear-Site-Data: '*'
Further reading Clear-Site-Data
Log your users out properly to protect their data from subsequent computer users
Ensure that logging out invalidates the access token/session identifier. It should no longer be usable if it later leaks to an attacker from browsing history/cache/memory/etc.
Additionally, if there is an SSO, don't forget to call the single logout endpoint correctly. Otherwise, logging out would be in vain since merely clicking the "log in" button would automatically log the user back as the SSO session is still active.
Finally, clear any cookies, HTML5 storage, etc., that you might have used. The Clear-Site-Data mentioned above is not yet supported by, e.g., Safari, so you will have to clear the data manually as well.
Use SessionStorage for JavaScript secrets to protect your user's data from subsequent computer users
It's like LocalStorage but unique for each tab and clears after the browser/tab is closed. So there's a chance of user data leaking to the next computer user.
Note If you want to have your user authenticated in multiple tabs of your application without logging in again, you will have to use events to sync the sessionStorage between the tabs.
Further reading Session Storage
Don't transmit sensitive data in the URL because URLs are not designed to be secret
URL addresses are not designed to be secret. They are, for example, displayed on the screen, saved to browsing history, leaked with referrer-headers, and saved on server logs. So don't put secrets in there.
Use a referrer policy to prevent URL addresses from leaking to other websites
Next on our checklist: referrer policies. By default, when you link to a website from your application, and a user clicks the link, web browsers will send a Referrer header to tell the website which website is linked to it. This header includes the entire URL, which can be a privacy issue at the least.
You can disable this behavior by specifying a Referrer-Policy header in your HTTP responses:
Referrer-Policy: no-referrer
Further reading
Use a unique domain name for your application to protect it from other applications under the same origin (and vice versa)
It is dangerous to host applications like this: https://www.example.com/app1/ and https://www.example.com/app2/. Browsers consider both of them to be of the same origin, that is, same host, port, and scheme. And being of the same origin, they will have full access to each other. Any vulnerabilities/malicious content affecting app1 will also put app2 in danger.
For this reason, give each application an origin of their own. So the solution could be https://app1.example.com/ and https://app2.example.com/.
Note Subdomains that share a parent can still set cookies for the entire domain. For example, app1.example.com can set a cookie on example.com which will then also be sent to app2.example.com. Being able to set cookies for a website will sometimes make attacks such as session fixation possible.
And if you are now wondering if all applications under .herokuapp.com are vulnerable, the answer is no because of the public suffix list. Also, you can protect cookies from getting overwritten by subdomains by naming your cookies `__Host-`.
Further reading
Don't use CORS unless you have to, and if you have to, be careful with it
The web browser's security model is largely based on the Same Origin Policy which prevents evil.example.com from reading your emails but still allows you to use jQuery from code.jquery.com. CORS or Cross Origin Resource Sharing is a means by which you can allow another website to violate that policy.
So if you decide that you need it, make sure you know what you are doing.
Further reading
Validate the origin
If you have api.example.com that needs to be accessed by GET requests from www.example.com then you can specify the following header on api.example.com:
Access-Control-Allow-Origin: https://www.example.com
If you have a public API (let's say a calculator that you want the entire Internet to use from client-side JavaScript), then you can specify a wildcard origin:
Access-Control-Allow-Origin: *
If you have multiple domains that you want to allow but not all (say you want to allow only Google and Facebook to access your API) then you will have to read the Origin header from the request, compare it to a list of allowed domains and then return a header as appropriate. It is recommended to use a well-vetted library for this instead of messing with the headers manually because a lot could go wrong.
Be mindful about the "allow credentials" option
CORS, by default, does not allow credentialed requests, that is, requests that carry the user's (session) cookies. But this can be allowed by the web server by specifying headers such as:
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true
This set of CORS headers is dangerous as it would allow https://www.example.com to fully access the website that specified the header just as the logged-in user would. So if you have to use it, be very careful.