Web
Web Security
Based heavily on Feross Aboukhadijeh's
CS 253
Stanford course.
##
Overview of this chapter - Same Origin Policy - Client-side Security - Cookies and Sessions - CSRF - XSS - Escaping - Phishing - Server-side Security - Authentication
##
Educational Objectives 1/6 On completion of this part, students will be able to: - cite the difference between "chiffrer" and "crypter" - explain why two pages from different origins should not be able to interfere with each other - and what differentiates two origins - explain how to allow specific origins to access a resource - explain how to prevent embedding of a resource - tell that CORS is enforced by the Browser
##
Educational Objectives 2/6 On completion of this part, students will be able to: - list headers that can be used to enable white-listing of specific origins - and use them in a server response - explain how to allow credentials in a request - describe how the browser checks if a server allows a request (preflight request) - cite the use cases of cookies - set a cookie from the client and from the server
##
Educational Objectives 3/6 On completion of this part, students will be able to: - explain the importance of the `Secure`, `HttpOnly`, `Domain` and `SameSite` attributes - cite what identifies a session ID - explain what is a CSRF attack and how to mitigate it - explain the difference between `Referer` and `Origin` - explain the difference between a session ID and a CSRF token
##
Educational Objectives 4/6 On completion of this part, students will be able to: - cite all type of XSS - explain if `innerHTML` can be used to inject scripts - explain how to and why escape HTML - cite dangerous places to inject scripts - cite and explain all the XSS mitigations/prevention techniques - how to set content security policy
##
Educational Objectives 5/6 On completion of this part, students will be able to: - cite different types of phishing and how to prevent them - cite security problems on the server side and how to prevent them - explain the difference between authentication and authorization - cite different types of authentication - cite good practices for password and explain them - explain response discrepency and how to implement it
##
Educational Objectives 6/6 On completion of this part, students will be able to: - explain why hashing passwords is better than encrypting them - explain why using known hashing library is better than creating your own - cite different mechanisms of authentication - explain what a JWT is and how it works - explain what oauth2 is and how it works
Quiz
##
`async` returning a non-Promise L'affirmation suivante est-elle vraie ou fausse ? "Il est possible de créer une fonction async capable d'être évaluée à une valeur qui n'est pas une Promise (sans utiliser await)" Par exemple, une telle fonction permettrait le code suivant ```js async function magicalFunction() { /* ... */ } let value = magicalFunction() // value is not a Promise ```
##
`async` returning a non-Promise : Solution L'affirmation suivante est-elle vraie ou fausse ? "Il est possible de créer une fonction async capable d'être évaluée à une valeur qui n'est pas une Promise (sans utiliser await)" Par exemple, une telle fonction permettrait le code suivant ```js async function magicalFunction() { /* ... */ } let value = magicalFunction() // value is not a Promise ``` Faux. Une fonction async retourne toujours une Promise, même si son corps ne contient pas de `return` explicite.
##
Throwing inside Promises Qu'arrive-t-il quand une erreur est lancée avec throw dans une fonction async ? ```js async function test() { throw new Error('Error') } test().catch(console.error) ```
##
Throwing inside Promises : Solution Qu'arrive-t-il quand une erreur est lancée avec throw dans une fonction async ? La Promise retournée par la fonction est rejetée avec l'erreur. ```js async function test() { throw new Error('Error') } test().catch(console.error) // Error: Error ```
##
`await` on rejected Promise Qu'affiche le code suivant ? ```js async function test() { console.log('Start'); await Promise.reject('Error'); console.log('End'); } test(); ```
##
`await` on rejected Promise : Solution Qu'affiche le code suivant ? ```js async function test() { console.log('Start'); await Promise.reject('Error'); console.log('End'); } test(); ``` `Start` puis une erreur est lancée.
##
Promise execution order 1 Qu'affiche le code suivant ? ```js Promise.resolve('Resolved').then(console.log); console.log('After promise'); ```
##
Promise execution order 1 : Solution Qu'affiche le code suivant ? ```js Promise.resolve('Resolved').then(console.log); console.log('After promise'); ``` `After promise` puis `Resolved`
##
Promise execution order 2 Qu'affiche le code suivant ? ```js async function fetchData() { const response = await fetch('https://jsonplaceholder.typicode.com/posts/1'); const data = await response.json(); console.log(data); } fetchData(); console.log('After fetch'); ```
##
Promise execution order 2 : Solution Qu'affiche le code suivant ? ```js async function fetchData() { const response = await fetch('https://jsonplaceholder.typicode.com/posts/1'); const data = await response.json(); console.log(data); } fetchData(); console.log('After fetch'); ``` `After fetch` puis les données. Il manque un `await` avant `fetchData()`. et il est impossible de le mettre car nous ne sommes pas dans une fonction async.
##
`await` keyword Quelles affirmations sont correctes au sujet du mot-clé await dans une fonction async ? 1. Cela évalue à une Promise qui a terminé 1. Cela lance une erreur si la Promise a échoué 1. Cela évalue au résultat de la Promise si elle a réussi 1. Cela fait attendre la fonction jusqu'à ce qu'une Promise termine (settle) 1. Cela met en pause l'execution du programme JavaScript 1. Cela catch toute erreur arrivant dans la fonction async 1. Cela évalue au résultat de la Promise ou à son erreur 1. Cela crée une nouvelle Promise
##
`await` keyword : Solution Quelles affirmations sont correctes au sujet du mot-clé await dans une fonction async ? 1. Cela évalue à une Promise qui a terminé **2. Cela lance une erreur si la Promise a échoué** **3. Cela évalue au résultat de la Promise si elle a réussi** **4. Cela fait attendre la fonction jusqu'à ce qu'une Promise termine (settle)** 5. Cela met en pause l'execution du programme JavaScript 6. Cela catch toute erreur arrivant dans la fonction async 7. Cela évalue au résultat de la Promise ou à son erreur 8. Cela crée une nouvelle Promise
##
Timeout promise Implémenter une fonction qui permet de faire échouer une Promise si elle prend trop de temps. Plus précisément, cette fonction prend en arguments - Une Promise qui risque de prendre du temps - Une timeoutError correspondant à l'erreur avec laquelle la Promise retournée doit échouer - Un nombre duration représentant le temps en millisecondes à attendre avant de faire échouer la Promise. Elle retourne une nouvelle Promise qui - settle comme la Promise donnée en arguments si celle-ci termine avant duration, ou - est rejetée si la Promise donnée en arguments n'a pas terminé après duration.
##
Timeout promise : Solution Implémenter une fonction qui permet de faire échouer une Promise si elle prend trop de temps. ```js function timeoutAfter(promise, timeoutError, duration) { return Promise.race([ promise, new Promise((resolve, reject) => { setTimeout(() => reject(timeoutError), duration) }) ]) } ```
##
Retry Promise Implémenter une fonction qui tente d'executer une Promise un certain nombre de fois avant d'abandonner et de rejeter. Plus précisément, la fonction retryUntil doit retourner une Promise et prendre en arguments - Une fonction retournant une Promise - Une error représentant ce avec quoi la Promise retournée doit échouer si celle passée en argument a dépassé le nombre d'essais autorisés - Un nombre trials représentant le nombre d'essais à faire avant d'échouer Cette fonction doit donc retourner une Promise qui - réussit comme la Promise donnée en argument si celle-ci réussit en moins de trials essais, - échoue avec error si la Promise donnée en argument a échoué trials fois d'affilée.
##
Retry Promise : Solution Implémenter une fonction qui tente d'executer une Promise un certain nombre de fois avant d'abandonner et de rejeter. ```js function retryUntil(getPromise, error, trials) { if (trials == 0) { return Promise.reject(error) } return getPromise().catch(() => retryUntil(getPromise, error, trials-1)) } ```
Disclaimer
##
It's illegal Unless you have expcilit permission from the owner of a website, it is illegal to pick at it. For example, in the US, it is a violation of the Computer Fraud and Abuse Act (CFAA) and potentially Wire Fraud, and can cost up to 20 years in prison. *This applies even to non-US citizens.* **Don't do it unless explicitly allowed to** Go on a Bug Bounty program instead.
##
Faux amis : Crypter, chiffrer - Chiffrer : il s'agit de transformer un message en un autre message, illisible sans la clé de déchiffrement. - Déchiffrer : il s'agit de transformer un message chiffré en un message lisible, à l'aide de la clé de déchiffrement. - Décrypter : il s'agit de transformer un message chiffré en un message lisible, sans la clé de déchiffrement. - Crypter : cela n'existe pas en français. Utilisez chiffrer. - Chiffrage : cela existe, mais dans le domaine de la comptabilité ou de la musique [NEXT - 30 avril 2024](https://next.ink/120490/edito-crypter-chiffrer-le-defi-de-la-vulgarisation/)
Same Origin Policy
## Same Origin Policy "Two pages from different origins should not be able to interfere with each other." - **Origin**: protocol, host, and port. For example, all following are different origins. - `http://www.example.com:80` - `https://www.example.com:80` - `http://.example.com:80` - **Interfere**: for example - JS execution accessing DOM or JS execution of another page - Modifying an `iframe`'s content - Fetching from another origin - Fetching from a subdomain - **Exceptions**: - Embedded static resources (images, scripts, styles, etc.) - `action` field of forms
## `iframe` An `iframe` allows embedding a page inside another page. ```html
```
## cross-origin `iframe` access prevented ```js const iframe = document.createElement('iframe') iframe.src = 'https://bank.com' // Forbidden unless currently on https://bank.com iframe.contentDocument.body.style.backgroundColor = 'red' // Alowed even if not on https://bank.com iframe.src = 'https://almost-bank.com' ```
## Communicating with `iframe` #### `document.domain` *[deprecated]* - Sets the domain of the current page, used in determining its origin. - Can only change to a parent domain. - If set on both pages to the same domain, they can communicate. **Problem**: if I change `login.heig-vd.ch` to `heig-vd.ch`, then any subdomain of `heig-vd.ch` can communicate with `login.heig-vd.ch`. #### `postMessage` - Allows message passing between iframe and embedder
## `postMessage` ```js // In embedder iframe.contentWindow.postMessage(
,
) ``` ```js // In embedded window.addEventListener('message', event => { if (event.origin !==
) { return } const msg = event.data // ... }) ``` **Important**: always verify `event.origin` and provide `target_origin`.
## Forbidding embedding The `X-Frame-Options` HTTP header can be used to forbid embedding of this resource. ```http X-Frame-Options: [DENY|SAMEORIGIN|ALLOW-FROM
] ``` - `DENY`: never allow embedding - `SAMEORIGIN`: allow embedding only if origin matches - `ALLOW-FROM
`: allow embedding only if origin matches
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
Cross-Origin Resource Sharing
(CORS)
##
Cross-Origin Resource Sharing (CORS) A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own.
https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
##
Cross-Origin Resource Sharing (CORS) While some cross-origin requests are allowed, most are blocked (e.g. fetch, POST requests, etc). CORS is an opt-in HTTP mechanism using HTTP headers that allows selected resources to be requested from a different origin. It is enforced **by the browser**, not the server. Its goal is to prevent client-side JavaScript from making unauthorized requests to a different origin. https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
##
Allowing specific origins The `Origin` and `Access-Control-Allow-Origin` headers combined enable whitelisting specific origins. In a request from `domain-a.com`, the `Origin` header tells `domain-b.com` where the request comes from. ```http GET / HTTP/1.1 Host: domain-b.com Origin: http://domain-a.com ``` In a response from `domain-b.com`, the `Access-Control-Allow-Origin` header tells the browser if it is allowed to include the resource. Here, the `*` wildcard means that the resource can be accessed by any domain. ```http HTTP/1.1 200 OK Access-Control-Allow-Origin: * ``` The `domain-b.com` server can also restrict access to `domain-a.com` only as follow: ```http HTTP/1.1 200 OK Access-Control-Allow-Origin: http://domain-a.com ``` https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS Note that the `Origin` header is sent to allow the server to make a more informed decision on its response.
#
CORS credentialed requests When making cross-origin requests, **CORS** does not send or receive cookies or HTTP authentication information by default. `fetch` and `XMLHttpRequest` include an option to include those credentials in the request. We call it a "credentialed" request. ```js fetch(url, {method: 'POST', credentials: 'include', body: JSON.stringify(data)}); ``` The server then responds with `Access-Control-Allow-Credentials: true` to allow the browser to include the response in the client's context. If missing, the response is discarded. https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#requests_with_credentials This can be useful, for example, in the case of a single sign-on (SSO) system, where the user is authenticated on a different domain than the one serving the content.
#
CORS preflight request For requests that can have an effect on the server (e.g. `POST`, `DELETE`, etc), the browser first needs to check if the server allows this kind of request. It does so by sending a **preflight request**, which is an `OPTIONS` HTTP request that includes the HTTP method and headers that would be used in the actual request. ```http OPTIONS / HTTP/1.1 Host: domain-b.com Origin: http://domain-a.com Access-Control-Request-Method: DELETE Access-Control-Request-Headers: X-PINGOTHER, Content-Type ``` If allowed, the server responds with the corresponding headers, allowing the browser to send the actual request. ```http HTTP/1.1 200 OK Access-Control-Allow-Origin: http://domain-a.com Access-Control-Allow-Methods: DELETE Access-Control-Allow-Headers: X-PINGOTHER, Content-Type ``` The browser can then send the actual request. This mechanism ensures requests that were not intended by the user never get to the server. It does not prevent the server from ever receiving unwanted requests. https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
Client-side security
Cookies and Sessions
##
Cookies Websites can store data in the browser from one request to another with **cookies**. With each request, the browser sends all cookies associated to that server. #### Use cases - **Session management**: store a session ID in a cookie, and store the session data on the server. - **Personalization**: store user preferences in a cookie. - **Tracking**: store user activity in a cookie.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
##
Anatomy of a cookie A cookie is defined by a **name** and a **value**. Note that a cookie is a single key-value pair. You thus usually have multiple cookies per domain. Optionally, it may also contain more attributes, like an expiration date, or some security directives.
##
HTTP Headers
The `Set-Cookie` HTTP response header is used to send cookies from the server to the user agent. ```http Set-Cookie:
=
;
=
;
=
; ... ``` The browser will send back all previously stored cookies to the server in the `Cookie` HTTP request header. ```http Cookie:
=
;
=
```
##
Cookie Attributes A cookies without `Expires` is called a session cookie, i.e., it is deleted when the client shuts down. **Expires** and **Max-Age**: expiration date for the cookie. If omitted, it is called a *session cookie* and expires with the current session (browser-dependent). ```http Set-Cookie: cookie=choco; Expires=Wed, 21 Oct 2020 07:28:00 GMT; ``` **HttpOnly**: the cookie should not be accessible to JavaScript. ```http Set-Cookie: cookie=choco; HttpOnly ``` **Secure**: the cookie should only be transmitted over HTTPS. ```http Set-Cookie: cookie=choco; Secure ``` **SameSite**: whether the cookie should be sent with cross-site requests. ```http Set-Cookie: cookie=choco; SameSite=[Strict|Lax|None] ``` And more we will cover later... https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies
##
Setting Cookies from the Client It is possible to access cookies from the same origin with javascript (unless they are `HttpOnly`). ```js // Object of the form {cookie1: value1, cookie2: value2, ...} var cookie = document.cookie; ``` Similarly, the value of the cookie can be modified from JavaScript. ```js // Add a new cookie document.cookie = "another_cookie=more_choco; SameSite=Strict"; ``` Note that `document.cookie`'s get and set methods are not symmetric. https://developer.mozilla.org/en-US/docs/Web/API/Document/cookie
##
Sessions The server stores data related to a browsing session (logins, shopping carts, tracking data). It identifies a browsing session with a **session ID**. A session ID - must be chosen and provided by the server - must be non-forgeable - must be unique for each session - is secretly shared with the client (through `Set-Cookie` over HTTPS) - is provided by the client with each request (through `Cookie` over HTTPS) - must expire after a while (`Expire` or `Max-Age`)
##
Important session cookie attributes If I can get your Session ID, I'm you. **`Secure`**: only send if over `HTTPS`.
(see [firesheep](https://en.wikipedia.org/wiki/Firesheep) extension to steal session IDs from Facebook and Twitter on WiFi communications, in 2010)
**`HttpOnly`**: prevent JS from accessing the Cookie. - If attacker controls my JS environment, they can't steal my session ID. **`Domain`**: specify a domain for which the cookie is valid. Its subdomains will be too. - Allows `login.heig-vd.ch` to set a cookie for `heig-vd.ch` and all its subdomains.
Client-side security
Cross-Site Request Forgery (CSRF)
## Cross-site Cookies Cookies are sent with every request to their origin, **regardless of the source of the request**, unless CORS prevents it (e.g. cross-origin `fetch`). If I make you click on `https://bank.com/transfer?to=me&amount=1000`, your browser will send your session cookie to `bank.com` with the request, which will succeed. This also works with POST requests: ```html
``` *(Such form could even be sent on load with JS: `document.forms[0].submit()`)*
## Mitigations #### `Referer` HTTP Header Contains the URL of the page that made the request. Server can then reject the request if from a different origin. **Problem**: Browser might do some caching and not send the request at all. - Use `Vary: Referer` or `Cache-Control: no-store` in response to prevent caching.
## Mitigations `Referer` and `Origin` may seem similar but differ in some important points - `Origin` only contains the origin (domain, protocol and port), not the full URL. - `Origin` is sent with all preflight requests and credentialed requests as part of CORS, while `Referer` has a different purpose. - `Referer` is used to track where a user comes from, including when clicking on a link. - `Referer` is mostly used for analytics, logging, cache control. Example of `Origin` header: ```http Origin: https://web-classroom.github.io ``` Example of `Referer` header: ```http Referer: https://web-classroom.github.io/lessons/cookies ```
## Mitigations #### `SameSite` Cookie Attribute Prevents the browser from sending the cookie with cross-site requests. - `SameSite=Strict`: only send with same origin requests - `SameSite=Lax`: also send when user navigates to the cookie's origin - `SameSite=None`: send with all requests
## Mitigations #### CSRF tokens Require the request to include an unforgeable secret token. ```html
``` The token is generated by the server upon sending the form, and stored in the session. ##### How does that differ from a session ID? The CSRF token is different from a session ID in that it is not sent in the cookie, but rather in the form itself. This means that requests to that form's action need to be made from the form's page, and therefore cannot be made from a different origin.
## Mitigations #### CSRF tokens Require the request to include an unforgeable secret token. ```html
``` The token is generated by the server upon sending the form, and stored in the session. ##### Why can an attacker not just use CSRF to get the form and then use the token to send the request? Because of the Same-Origin Policy, an attacker cannot read the response from the server, and thus cannot read the token (unless the server allows it with CORS, which is highly unlikely).
## General Thoughts - Never trust data from the client. - Always set `Secure; HttpOnly; SameSite=Strict; Expires=...` on your cookies. - Use CSRF tokens for all requests that can have an effect on the server.
Client-side security
Cross-Site Scripting (XSS)
## Types of XSS **Reflected XSS**: code is part of the request. Victim clicks the link and the code is executed. **Stored XSS**: code is persisted on the server. Victim only has to view the page. **DOM-based XSS**: code is displayed by JS on the client.
## Reflected XSS example ```js app.get('/', (req, res) => { const user = req.query.user res.send("
Hello, ${user}
") }) ``` Vulnerable to a link ending with `?user=%3Cscript%3Ealert(%27document.cookie%27)%3C/script%3E`.
## DOM-based XSS example ```js let name = // get from unsafe place document.getElementById('greeting').innerHTML = `Hello, ${name}` ``` Here, giving `` as `name` would not work, because most browsers will not execute the script tag when setting `innerHTML`. However they will evaluate tags like `img` or `a`. So the following input would work: ```text
``` Can be mitigated by inserting user data into the DOM with `textContent` instead of `innerHTML`.
## Reflected XSS example mitigation First rule: **never trust user data**. Second rule: **escape user data**. ```js import htmlEscape from 'html-escape' app.get('/', (req, res) => { const userHtml = htmlEscape(req.query.user) res.send("
Hello, ${userHtml}
") }) ``` *We will talk more about escaping later.*
## Dangerous places - Element content Inside an HTML element's content. ```html
You searched for USER_DATA
``` Can be exploited with ```text ``` **Mitigation**: Escape `<` and `&`.
## Dangerous places - Attribute values Inside the value of an HTML element attribute. ```html
``` Can be exploited with ```text Nobody' onload='alert(document.cookie) ``` **Mitigation**: Escape single quotes, double quotes and `&`.
## Dangerous places - `src` and `href` attributes Data and Javascript URLs can be used to execute arbitrary code. #### Data URL Allows inlining of media data. Has the form `data:
,
`. ```text data:text/html, ``` #### Javascript URL Allows executing JS code instead of loading a resource. Has the form `javascript:
`. ```text
Click me!
```
## Safe places with escaping Escaping is only safe into - HTML element bodies (escape `<` and `&`) - HTML attributes (if surrounded by quotes, escape `'`, `"` and `&`)
## Not Safe in JS strings? Why can I not escape `'`, `"` and `\` and then insert into a JS string? ```js let userData = // ... let safeData = escape(userData) // removes quotes and '\' let html = ` ` ```
## Not Safe in JS strings? : Solution Why can I not escape `'`, `"` and `\` and then insert into a JS string? ```js let userData = // ... let safeData = escape(userData) // removes quotes and '\' let html = ` ` ``` There are two layers of parsing here, first HTML, then JS. One exploit is thus simply to provide ```text ```
## Escaping into code through HEX Never safe to insert user data into JS code. If necessary, one solution is to - encode user data to HEX value, - insert HEX value into code, - let code decode HEX back to user data. ```js let hexifiedUserData = hexEncode(userData) let html = ` ` ```
## Escaping into code through `template` Escaping into HTML is safe, so another solution is to pass user data through HTML. `
` elements allow storing HTML code in the DOM without rendering it. ```js let userData = // ... let htmlSafeData = htmlEscape(userData) let html = `
${htmlSafeData}
```
## Escaping hell Escaping is hard. ```html
``` Three rounds of parsing in this example - HTML parses `div` and extracts `onclick` value - Upon clicking, JS parses and executes `onclick` value - After 1 second, JS parses and executes `setTimeout` first argument `USER_DATA` needs to be escaped three times : JS, JS and finally HTML. Any mistake along the way is a vulnerability.
## Variable injection JavaScript creates a global variable for every HTML element with an `id` attribute. ```html
...
``` Although limited, allows impact on execution flow, if attacker can control an `id` attribute. ```html
...
``` Setting `ESCAPED_USER_DATA` to `connected` affects the execution flow.
## XSS mitigations - Always escape on the way out.
Don't trust your stored data, someone could have managed to insert unescaped data into it.
- Let built-in function do it (e.g. `htmlEscape`, ejs templates, etc). - Don't put user data in stupid places (e.g. inside a `<script>` tag...). - Stack defenses: assume the attacker is already in your JS environment. - Ask password or 2FA for important actions (change password, delete account, etc). - Send emails for audit logs. - Defend user's cookies with `HttpOnly`. - Use `Content-Security-Policy` to prevent loading of external scripts.
CSP prevents our site from making requests to other sites or executing inlined JS. Useful when you know you don't need cross-site references.
Are CSRF tokens a mitigation against XSS?
## XSS mitigations : Solution Are CSRF tokens a mitigation against XSS? No, they are not related: CSRF tokens prevent requests from other origins, XSS is about executing code from the same origin, so XSS injected code can find the CSRF token and use it.
## Content Security Policy (CSP) CSP prevents our site from sending requests to other sites, or executing inlined JS. For instance, the following policy trusts subresources from the current domain and images from instagram: ```http Content-Security-Policy: default-src 'self'; img-src instagram.com ``` A CSP compatible browser will then only execute scripts loaded in source files received from those allowlisted domains Prevents the transmission of sensitive data to a untrusted server. https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
##
Hands on XSS Play around with XSS challenges on - https://xss.pwnfunction.com/warmups/ - https://xss-game.appspot.com/
Client-side security
Phishing
## The problem of phishing > “Security solutions have a technological component, but security is fundamentally a people problem.” > > -- Bruce Schneier
## Internationalized Domain Names (IDN) Hostnames may contain unicode characters. They are then transcoded into an ASCII subset called "punycode". **Problem**: `"xn--pple-43d.com"` is `"apple.com"` in punycode. So https://www.аррӏе.com/ is not https://www.apple.com/. **Solution**:
show punycode when fishy (Safari does, Firefox and Chrome don't).
## Confusion using subdomains Send a link like the following to a user: ```text http://apple.com-webappsuserid29348325limited.active-userid.com/webapps/89980/ ``` Actual domain is `active-userid.com`. **Solution**:
gray out subdomains in browser search bar.
## Faking UI elements **Replace entire screen** with HTML5 fullscreen API: https://feross.org/html5-fullscreen-api-attack/ **Replace UI elements** with clever tricks: https://jameshfisher.com/2019/04/27/the-inception-bar-a-new-phishing-method/
## Mitigations - Password managers don't get fooled - Yubikey and other hardware keys don't get fooled
Server-side Security
## Assumptions The attacker can send any data to the server. They are not restricted to the UI you provide.
## DNS - Hijack a DNS resolver (by compromizing user account at DNS provider, hijacking router, etc) - Change a user's DNS settings to contact your resolver instead of their own - DNS queries are plaintext. ISPs know what you visit, and some sell this data. - DNS-over-HTTPS (DoH) is a solution, but not widely adopted yet.
## Code injection - XSS - Shell - SQL - Blind SQL injection (no output, but ability to ask true/false by observing response timing or error messages) - Mitigations: parameterized queries, ORMs. An example of blind SQL injection [here](https://portswigger.net/web-security/sql-injection/blind).
## Leaks No need to attack, the server does the job for the attacker. - Error messages and logs can hold sensitive information - Over-fetching: sending more data than requested - 2020: [Instagram leaks birthdays and email addresses](https://securityreport.com/facebook-fixes-instagram-bug-that-leaked-users-private-email-address-and-birthday/)
## Takeaways - Never trust user data - Complexity is the enemy of security - Explicit code is better than clever code - Fail early - Code defensively
Authentication
## Authentication - **Identification**: who are you? - **Authentication**: prove it. - **Authorization**: what are you allowed to do? Authentication can be done using - what you know (password) - what you have (hardware key) - who you are (biometrics)
## Password ~~good~~ practices
## Password good practices have changed - Complex ≠ Strong : passphrases are much stronger than 10-character randomness - Changing passwords often encourages weak passwords - Length is the most important factor - Copy-pasting is not a problem - Security questions are terrible
## Implementation good practices - Check new passwords against known data breaches - Limit rate and number of failed attempts - Use 2FA - Do not store passwords in plain text - Do not restrict password alphabet or length - Never send passwords by email or SMS, it's plaintext - Use HTTPS, obviously
## Network-based guessing attacks - Brute-force - Credential stuffing (use credentials leaked from elsewhere) - Password spraying (try common passwords against many accounts) - Dictionary attacks (try common passwords against one account)
## Response Discrepency **All errors should look the same.** Otherwise information can be inferred. - Don't say why login failed - Don't say whether email is unknown when resetting password - Don't say whether email is already in use when creating account - Don't use inconsistent HTTP status codes on error - Don't return early on authentication failure Tradeoff between UX and security.
## Mitigations - CAPTCHAs have new problems - Re-authenticate for sensitive features (change password, delete account, etc) - Check HaveIBeenPwned regularly (password managers usually do that) - NEVER store passwords in plain text
## Storing passwords **Don't encrypt passwords, hash them.** Hashing is not enough though. - Identical passwords have identical hashes - Rainbow tables allow easy reversing for common passwords **Add salt**: salt is random data added to the password before hashing. It is stored in plaintext alongside the hash. - Identical passwords have different hashes - Entropy greatly increases, so rainbow tables are useless **Best solution**: use existing libraries (bcrypt, scrypt, argon2, etc) **"DON'T ROLL YOUR OWN CRYPTO"** applies here.
## Storing passwords - Encryption Encrypting passwords is reversible, hashing is not. If the host is compromised, the decryption key will most likely be too, making the encryption useless.
## Authentication Mechanisms The `Authentication` header allows sending authentication information to the server using different schemes: - Basic: username and password sent in base64 encoding (~plaintext). - Bearer: a token sent in plaintext, with the server choosing the protocol for token creation and verification (e.g., HMAC or JWT). The `X-API-Key` header is used to send an API key to the server. Other authentication mechanisms include: - [OAuth2](https://oauth.net/2/): authorizes third-party apps to access user data without sharing credentials. - [WebAuthN](https://webauthn.io/): authenticates users using hardware keys.
## Authentication Mechanisms The `Authentication` header is a generic header that can be used to send authentication information to the server. It can hold authentication data of any form; the server is responsible for parsing it and verifying it. Its value can follow different [authentication schemes](https://www.iana.org/assignments/http-authschemes/http-authschemes.xhtml). - Basic: username and password are encoded in base64 and sent in the header. It is not secure, as it is sent in plaintext. - Bearer: a token is sent in the header, used to describe the user. That token is generated by the server, and can be verified by the server. The protocol used to create and verify this token is freely chosen by the server. Some examples are - HMAC: the token is a hash of the user's data, signed with a secret key. - JWT: the token is a JSON object containing the user's data, signed with a secret key. Often times, JWT uses HMAC to sign its JSON.
## One size does not fit all! The choice of an authentication method typically varies depending on: - The kind of entity you authenticate (Human or Machine) - The kind of service you provide (API or Web Application) - The kind of web application you devise (SPA or MPA) - The needs in terms of security (immediate revokation, time-to-live, etc.) - The needs in terms of scalability (number of users, number of requests, etc.) - The needs in terms of user experience (login, logout, etc.) - etc.
## Authorization Mechanisms In Web applications, authorization mechanisms often rely on the notions of: - Roles (admin, editor, user) - Ownership (does this resource, object or attribute belong to that user) In practice, authorization mechanisms are often hard-coded, which gives a lot of flexibility. However, it is also possible to rely on authorisation patterns, such as: - [Access-Control List (ACL)](https://en.wikipedia.org/wiki/Access-control_list) - [Role-based access control (RBAC)](https://en.wikipedia.org/wiki/Role-based_access_control) - [Attribute-based access control (ABAC)](https://en.wikipedia.org/wiki/Attribute-based_access_control) In ExpressJS, the [express-acl](https://www.npmjs.com/package/express-acl) and [express-rbac](https://www.npmjs.com/package/express-rbac) packages provides authorization middlewares.
##
JSON Web Token (JWT) Standardized method for representing claims in a self-contained and secure manner. It contains - a header with metadata (algorithm, type) - a payload with the claims - a signature to verify the integrity of the token It is generally created by the server and given to a client. The client then sends it back to the server with each sensitive request, usually in the `Authorization` header: ```http Authorization: Bearer
``` It can thus be used for both authentication and authorization (by including the user's roles in the payload). - **Scalability**: Because it is self-contained, it requires no server-side state. - **No-secrecy**: The payload is **not** encrypted, so it should not contain sensitive data. - **Revocation**: JWT tokens cannot be revoked by the server, unless through blacklisting. - **REST API keys**: JWT tokens can be used to authenticate and authorize API callers statelessly.
##
JSON Web Token (JWT) - Why not cookies? Sending it through the `Authorization` header gives more control over *when* it is shared, and *with whome*. It can be shared cross-origin if desired, and is not sent with every request. https://jwt.io/
##
OAuth2 Standard for authorizing third-party applications to access a user's data without sharing their credentials. It handles relations between - The **Resource Owner** that owns the data and can grant access to it - The **Resource Server** that hosts the owner's data - The **Client** that wants to access the owner's data - The **Authorization Server** that issues access tokens upon request by the client, and authorization by the owner. It uses an **Access Token** to represent the authorization granted by the user to the third-party application. That token is often a JWT token. https://auth0.com/docs/api-auth/which-oauth-flow-to-use
##
Configure an Authentication Middleware Clone the `example-passport` repository from the `web-classroom` organization. https://github.com/web-classroom/example-passport It illustrates how: - Local authentication can be configured in express with [Passport](http://www.passportjs.org/) - Github can be used for authentication (via oauth2)