Security is a big topic in web development, and while it’s usually not the FOCUS in many places, it’s still very crucial. I encountered some questions on it during (purely) dev interviews. While I did some assignments on actually carrying out the attacks, how do we actually defend against them?
XSS (Cross Site Scripting)
This happens when an attacker injects malicious scripts into web pages viewed by other users. The attacker can steal cookies, session tokens, or other sensitive information.
Types of XSS:
| Types | Description |
|---|---|
| Stored XSS | The attacker injects a script that is permanently stored on the target server. |
| Reflected XSS | The attacker injects a script that is reflected off the web server. |
| DOM-based XSS | The attacker injects a script that is executed by the victim’s browser. |
One thing to note here is the vulnerability lies here is in the server. That means if your server does not protect against injection or some way in which the hacker can inject code, then the server is vulnerable to XSS. Users will then be vulnerable when they visit the site.
Ways to prevent XSS:
-
Input validation
- Sanitize user input. To be more precise, you should only allow certain characters in the input. For example, if you’re expecting a username, you should only allow alphanumeric characters and underscores. There are many libraries that can help with this, such as
DOMPurifyorSanitize.js.
- Sanitize user input. To be more precise, you should only allow certain characters in the input. For example, if you’re expecting a username, you should only allow alphanumeric characters and underscores. There are many libraries that can help with this, such as
-
Output encoding
- Encode user input before displaying it. What this means is that if you have a string like
<script>alert('XSS')</script>, you should encode it to<script>alert('XSS')</script>. This way, the browser will not execute the script.
- Encode user input before displaying it. What this means is that if you have a string like
-
Content Security Policy (CSP)
- Prevents XSS by restricting the sources of content that the browser can load. To implement this, you can add a
Content-Security-Policyheader to your server response. This header tells the browser which sources of content are allowed to be loaded. This will bring a whole lot of inconvenience so below, we’ll talk abit more about it.
- Prevents XSS by restricting the sources of content that the browser can load. To implement this, you can add a
-
HttpOnly flag
- Prevents cookies from being accessed through JavaScript. This can be done by setting the
HttpOnlyflag on the cookie, so that the cookie cannot be accessed by JavaScript.
- Prevents cookies from being accessed through JavaScript. This can be done by setting the
CSRF (Cross Site Request Forgery)
This happens when an attacker tricks a user into performing actions on a website without their knowledge or consent.
Example:
- The attacker sends a link to the victim that contains a request to change the victim’s password on a website.
- If the victim clicks on the link while logged into the website, the request will be executed.
The difference between XSS and CSRF is that XSS is executed in the context of the victim’s browser, while CSRF is executed in the context of the attacker’s server.
Ways to prevent CSRF:
-
Anti-CSRF tokens (different from JWT)
- Include a unique token in each request that is verified by the server. The token would be unique for each request and should be verified by the server. This can be done by including a hidden input field in the form that contains the token.
-
SameSite cookie attribute
- Prevents the browser from sending cookies in cross-site requests.
-
CORS (Cross-Origin Resource Sharing)
- Prevents cross-origin requests from being made to the server. The server can send a response with the
Access-Control-Allow-Originheader set to*, which allows any origin to access the resource.
- Prevents cross-origin requests from being made to the server. The server can send a response with the
Additional info on CORS
CORS are a pain really for developers. But they exist for a reason. They are used to prevent cross-origin requests from being made to the server. This is done by checking the origin of the request and comparing it to the origin of the server.
Two websites are considered to have the same origin if they have the same
- protocol,
- domain, and
- port number.
If the origins are different, the browser will block the request unless the server allows it.
Eg. you try to fetch an image from a different domain, the browser will block the request unless the server allows it.
Ways to bypass CORS: (hackish ways I’ve done to at least get my site to work 😭 and they’re small scale projects!):
(Not recommended)
-
Using a proxy server
- The proxy server fetches the data from the server and sends it to the client. Setting up a proxy server is inconvenient though, to implement it, you gotta set up an additional server. You can use a service like
cors-anywhereto set up a proxy server.
- The proxy server fetches the data from the server and sends it to the client. Setting up a proxy server is inconvenient though, to implement it, you gotta set up an additional server. You can use a service like
-
JSONP (JSON with Padding)
- This is sort of a less known method?
It is a method for sending JSON data without being blocked by CORS. Essentially, the server wraps the JSON data in a function call and sends it back to the client. This is because
<script>tags are not subject to the same-origin policy.
- This is sort of a less known method?
It is a method for sending JSON data without being blocked by CORS. Essentially, the server wraps the JSON data in a function call and sends it back to the client. This is because
-
CORS misconfiguration
- Add the
Access-Control-Allow-Originheader to the server response. deadly but it works.
- Add the
-
iframe
iframeHTML elements are not subject to the same-origin policy.iframeare basically like a window to another website. You can use this to load a page from a different domain and then use JavaScript to access the content of theiframe. It doesn’t solve everything and maybe doesn’t align with the use case, but it’s a hacky way to get around CORS.