Web_Security
Web_Security
https://stevekinney.net/courses/security
Steve Kinney
The good part of the Web
is also the bad part.
Even on it’s best day:
The Web is weird.
Very few—if any—of us think
that security is not important.
In the Real World®,
it’s all about trade-offs.
“Oh, well we had to do $thing in
order to get $feature working.”
Set-Cookie: username=bobbytables;
HTTP Header Key Value
Cookies
The browser sends back the cookie on subsequent requests.
Cookie: username=bobbytables;
HTTP Header Key Value
Cookies
Accessing cookies from the browser—sometimes.
Cookie Attributes
Expiration Dates.
Without some kind of expiration, cookies last for the duration of the
session.
Cookies
How to delete a cookie.
You can’t.
But, you can set it’s expiration date to some time in the past.
The Path attribute indicates a URL path that must exist in the
requested URL in order to send the Cookie header.
Set-Cookie: username=bobbytables;
Domain=.frontendmasters.com;
Cookie Jar
Same Origin Policy
A security measure implemented by web browsers to restrict how documents or
scripts loaded from one origin interact with resources from other origins.
Two resources from different sites shouldn’t be able to interfere with each other.
Protocol + Host + Port
Same Origin Policy
How it works.
If those three things are the same, then the browser considers the two
resources to have the same origin.
Same Origin Policy
Ways around it.
Proxies
PostMessage API
WebSockets
document.domain
Cookies
Some common vulnerabilities.
example.com
login.example.com
Target: Applications that pass unsafe user inputs to system shell commands.
if (stderr) {
return res.status(500).send(stderr);
}
res.send(stdout);
});
});
Second: Use a built-in Node module (e.g. fs) if you really need to.
Injection: RCE typically occurs when the user input is not properly
sanitized.
Input Validation: Always validate and sanitize user inputs. Use strict data types and
constraints.
Don’t Do Dangerous Stuff: Avoid using eval(), Function(), exec(), and other
potentially dangerous Node.js functions.
Use Security Libraries: Utilize libraries such as DOMPurify for sanitizing HTML. Use
sandboxed environments like VM2 for executing untrusted code.
Principle of Least Privilege: Run services with the minimal required permissions. Do not
run your application as a root user.
Update Your Stuff: Keep Node.js and all dependencies up-to-date to mitigate known
vulnerabilities.
Cross-Site Request Forgery
A vulnerability that allows an attacker to make unauthorized requests on the
user’s behalf.
http://twitter.com/share/update?status=Your%20Message
Case Study
Netflix (2006)
Netflix had a GET endpoint that allowed you to add a DVD to your queue. It was
as easy as…
<img src="http://www.netflix.com/JSON/AddToQueue?
movieid=70110672" />
Case Study
YouTube (2008)
They also discovered CSRF vulnerabilities in nearly every action a user
can perform on YouTube.
Case Study
Host: supercoolwebsite.com
Content-Type: application/x-www-form-urlencoded
Cookie: sessionId=de4db33f
delete=1
function createAndSubmitForm() {
const form = document.createElement('form');
form.method = 'POST';
form.action = 'https://example.com/delete-account';
form.style.display = 'none';
document.body.appendChild(form);
form.submit();
}
createAndSubmitForm();
Cross-Site Request Forgery
The three ingredients to the recipe.
Implementing a CSRF
Attack
Cookie Attributes
The SameSite attribute.
This can be important for legitimate use cases (e.g., a link in an email
or from another domain).
Cookie Attributes
Lax versus Strict: A balance of trade-offs.
<input type="hidden"
name="csrf"
value=“3964ccc5b64f54696134
3c57cf" required />
CSRF Tokens
Being unpredictable as a defense mechanism.
<meta name="csrf-token"
content=“3964ccc5b64f546961
343c57cf">
Per Session vs. Per
Request
Request Body vs.
Request Headers
CSRF Tokens
Some anti-patterns.
application/x-www-form-urlencoded
multipart/form-data
text/plain
Access-Control-Allow-Origin: https://alloweddomain.com
Cross-Origin Resource Sharing
The headers.
Access-Control-Allow-Headers: X-SUPER-CUSTOM-HEADER,
Content-Type
Cross-Origin Resource Sharing
The headers.
Access-Control-Allow-Credentials: true
Cross-Origin Resource Sharing
Some anti-patterns.
Don’t try to swap out headers that are subject to CORS (e.g. PUT,
PATCH, DELETE) for simpler ones (e.g. GET, POST).
Cross-Origin Resource Sharing
Some anti-patterns.
Only use a wildcard for your CORS policy if you never need to allow
credentials.
Request Security Headers
Some additional headers set by the browsers.
Anything that starts with sec- is a header that JavaScript can’t touch,
which means you can assume it’s safe and hasn’t been tampered with.
Request Security Headers
sec-fetch-site
cross-site: The request initiator and the server hosting the resource
have a different origin and site.
same-site: The request initiator have the same site—but, this could
be a different origin.
none: The user did this. They entered a URL into the address bar or
opened a bookmark or dragged a file into the browser window.
Request Security Headers
sec-fetch-dest
iframe for—umm—iframes.
Request Security Headers
Two additional headers.
This occurs when an attacker sends malicious code, generally in the form of a
browser-side script, to a different end user.
Cross-Site Scripting
It comes in a few different avors.
The payload would then plant itself on that user’s profile page. Within just 20
hours, it spread to over a million users.
Source: https://samy.pl/myspace/
The Samy Worm
How it worked.
But, in that previous example, you’d already used up single and double
quotes. So, working with strings would be hard. But, you could hide
the code in another attribute.
<div
id="mycode"
expr="alert('hah!')"
style="background: url('javascript:eval(document.all.mycode.expr)')"
></div>
The Samy Worm
How it worked.
And, browsers were totally cool with that new line jammed in there.
The Samy Worm
How it worked.
String.fromCharCode(34)
The Samy Worm
How it worked.
As you can imagine, MySpace was also not found of things like
innerHTML. But, apparently not eval?
alert(eval('document.body.inne' + 'rHTML'));
From here, he had access to the full source of the page and the ability
to make AJAX requests with the users cookie.
When users hovered over these links, the worm retweeted itself.
Source: https://blog.x.com/o cial/en_us/a/2009/wily-weekend-worms.html
ffi
The Twitter Worm
How it worked.
<a
href="https://frontendmasters"
class="tweet-url web"
rel="nofollow">
https://frontendmasters
</a> is the best!
The Twitter Worm
How it worked.
http://lol.no/@";onmouseover=";$
('textarea:first').val(this.innerHTML);$('.status-
update-form').submit();"class="modal-overlay"/
Now, if you hovered over that link, you’d end up posting the same
content. Whoops.
Case Study
eBay (2015–2016)
eBay had supported a redirectTo query parameter. The problem was
that the pages you could redirect to didn’t necessarily validate the
query parameters in the URL that the user was redirected to—allowing
for XSS attacks.
Case Study
McDonald’s (2017)
We can blame a flaw in Angular’s sandbox that allowed the attacker to
execute code via a query parameter or we can blame the fact that
McDonald’s had made the interesting choice to decrypt passwords
client-side.
Case Study
The attack affected almost 500,000 customers of British Airways, of which almost
250,000 had their names, addresses, credit card numbers and CVV cards stolen
Case Study
Fortnite (2019)
Fortnite had a legacy page with serious vulnerabilities that went unnoticed. The
vulnerability allowed an attacker to get access to the data of all Fortnite users.
Source: https://
thehackernews.com/2011/06/
xss-attack-on-cia-central-
itelligence.html
Case Study
GitLab (2024)
Tracked as CVE-2024-4835. This exploit took advantage of a flaw in
their VS Code plugin that allowed an attacker to craft a malicious page
to “exfiltrate sensitive user information.”
Source: https://www.youtube.com/watch?v=2GtbY1XWGlQ
Cross-Site Scripting
How it works.
Data Theft and Manipulation: Since the script executes as if it were part
of the original website, it can steal cookies, session tokens, or other
sensitive information.
Demonstration
Content Security Policy (CSP): Implement CSP headers to restrict sources from
where scripts, styles, and other resources can be loaded.
Use Safe Methods: Avoid using functions that allow raw HTML input like
innerHTML or document.write.
Libraries and Frameworks: Utilize established libraries and frameworks that auto-
escape content and provide built-in protection mechanisms.
Safe Sink
A place where you can out data without the risk of it being vulnerable
to XSS. For our purposes, these are places where the browser promises
not to execute any code you give it.
Safe Sinks
As compared to unsafe sinks.
element.textContent = element.innerHTML =
randomUserInput is a safe sink. randomUserInput is not a safe
The browser will not execute this sink. The will—sometimes—
code. execute this code.
Safe Sinks
Some DOM methods are considered safe sinks.
element.textContent
element.insertAdjacentText
element.className
element.setAttribute
element.value
document.createTextNode
document.createElement
Source: https://github.com/cure53/DOMPurify/blob/main/src/attrs.js
Source: https://github.com/payloadbox/xss-payload-list
And now…
Helpful Tools That Can Be Bad if You Rely on
Them Exclusively.
Content Security Policy
A security feature that helps prevent a range of attacks, including
Cross-Site Scripting (XSS) and data injection attacks. CSP works by
allowing web developers to control the resources the browser is
allowed to load for their site.
CSP can be thought of a
second layer defense.
Content Security Policy
How it works.
Alternatively, you can use a meta tag in the head of your HTML
document.
<meta http-equiv="Content-Security-Policy"
content="script-src 'self' https://trusted.cdn.com">
Demonstration
Yes, you should totally sanitize all user inputs and defensively code as best
you possibly can. But, having a strong Content Security Policy gives you one
extra layer of protection.
So, what if you can’t use a CSP because it will “break stuff.”
This will allow to collect data and prioritize which pages need some
love.
PSA: DO NOT use X-Content-
Security-Policy or X-WebKit-CSP.
Strict CSP
Really battening down the hatches.
<style nonce=“rAnd0m”></style>
Using a Nonce for CSP
The pros and cons.
It’s a small value, which means You’ll need to generate your pages
your headers will be smaller programmatically. This is
and you’ll need to send fewer obviously easier if you’re using
bytes over the wire. server-generated pages.
You won’t need to update Since the initial page has the
anything if the content of your nonce, you cannot cache the
script files change. HTML.
Using a Hash for CSP
The alternative to using a nonce.
<script src=“https://code.jquery.com/
jquery-3.7.1.min.js" integrity="sha256-/JqT3SQfawRcv/
BIHPThkBvs0OEvtFFmqPF/lYI/Cxo="
crossorigin="anonymous"></script>
Using a Hash for CSP
The ups and the downs.
You don’t have to have any Depending on how many files you
special infrastructure outside of have, this can get big.
generating a hash.
If you or anyone else changes the
file, then it won’t load since the
hash is no longer valid.
Opacity & Positioning: The iframe is made invisible (e.g. the opacity is
set to 0) or positioned behind other content.
User Interaction: The user believes they are interacting with the visible
elements but are actually interacting with the concealed iframe.
Clickjacking
An example.
The Target: A banking site where a user logs in and clicks a button to
transfer funds.
The Trap: A visible button on the attacker's site saying “Click to Win a
Prize!”
The Attack: Our friendly user clicks the button, which actually triggers
the transfer funds button in the invisible iframe.
Clickjacking
Antidote: Use the X-Frame-Options Header.
DENY: Prevents the site from being displayed in iframe elements altogether.
SAMEORIGIN: Allows the site to be framed only by pages on the same origin.
next();
});
=
Demonstration
Clickjacking
postMessage
postMessage is a method that allows for secure cross-origin
communication between Window objects. It enables data exchange
between a page and an iframe irrespective of their origins.
postMessage Vulnerability
A security issue arising when postMessage is misused or improperly
validated. This can lead to data leakage, script injection, or cross-site
scripting.
postMessage Vulnerabilities
How it works and what can go wrong.
Encrypting, but leaving the keys under the doormat: Poor handling of
encryption keys, such as hardcoding keys in the code.
Encrypting, but too little and too late: Failing to encrypt data transmitted
between the server and the client. Why aren’t you using HTTPS?
Data Encryption
An ounce of prevention.
Hide your keys: Store encryption keys securely, not in the source code
or environment variables.
You can use various data as claims, tailoring security and functionality for your
application.
Claims
Types of claims.
Public Claims: Users can define these. Yet, to avoid conflicts, they
should be registered with IANA or use unique URIs.
Private Claims: These are custom claims for specific uses, like sharing
a user's permissions.
JSON Web Tokens
A super helpful example.
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY
3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
JWTs Versus Session IDs
Storage of User Data
JWTs are more scalable. They don't Scaling is harder with Session
require server-side session storage. IDs. Every server that could serve
a user's request must access their
This makes them perfect for
session data.
distributed systems.
In these setups, users work with This often means using shared
many servers in a cloud. storage or replicating sessions.
These methods can complicate
By skipping session storage, servers
load balancing and increase
save resources. This also simplifies
overhead.
load balancing.
JWTs Versus Session IDs
Security Considerations
The JWT controls its own With session IDs, the server
expiration. manages expiration.
An attacker can modify the token header to specify alg: 'none' and
remove the signature, potentially bypassing the verification process.
Add SameSite: Set the attribute to Strict or Lax to reduce CSRF risks.
Lax allows some cross-site usage.
JWT Best Practices
Use short-lived JWTs and refresh tokens
Refresh Tokens: Keep sessions active with refresh tokens. Store them
securely and use them to issue new access tokens.
JWT Best Practices
Do the stuff that we all know we’re supposed to do.
Avoid Sensitive Info: Don't store sensitive data in JWTs. They are
easily decoded.
Log and Monitor Usage: Track token activities for security. Look out
for unusual access patterns.
Using a JWT
Strict-Transport-Security
A header that allows you to enforce that all of your traffic goes through
HTTPS.
“Be aware that inclusion in the preload list cannot
really be undone. You can request to be removed, but
it will take months for the deleted entry to reach users
with a Chrome update and we cannot make
guarantees about other browser vendors. Don't
request inclusion unless you're sure that you can
support HTTPS for the long term.”