-
-
Notifications
You must be signed in to change notification settings - Fork 10
refactor articles, add article index, opengraph metadata #17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| import type { Metadata } from 'next'; | ||
| import Link from 'next/link'; | ||
|
|
||
| import { articles } from '..'; | ||
|
|
||
| export const dynamicParams = false; | ||
|
|
||
| export async function generateStaticParams() { | ||
| return Object.keys(articles).map((slug) => ({ | ||
| slug, | ||
| })); | ||
| } | ||
|
|
||
| type Props = { params: Promise<{ slug: string }> }; | ||
|
|
||
| export async function generateMetadata({ params }: Props): Promise<Metadata> { | ||
| const { slug } = await params; | ||
| const article = articles[slug]; | ||
|
|
||
| return { | ||
| title: article.title, | ||
| description: article.description, | ||
| openGraph: { | ||
| title: article.title, | ||
| description: article.description, | ||
| type: 'article', | ||
| publishedTime: article.date.toISOString(), | ||
| }, | ||
| }; | ||
| } | ||
|
|
||
| export default async function Page({ params }: Props) { | ||
| const { slug } = await params; | ||
| const article = articles[slug]; | ||
|
|
||
| return ( | ||
| <div className="flex flex-col gap-4"> | ||
| <div className="translucent-snow p-4 rounded-lg [&_p]:my-4"> | ||
| <h1>{article.title}</h1> | ||
| <div className="text-sm text-english-violet"> | ||
| Published{' '} | ||
| {article.date.toLocaleDateString('en-US', { | ||
| weekday: 'long', | ||
| year: 'numeric', | ||
| month: 'long', | ||
| day: 'numeric', | ||
| })} | ||
| </div> | ||
| {article.content} | ||
| </div> | ||
|
|
||
| {article.relatedLinks && ( | ||
| <div className="translucent-snow p-4 rounded-lg"> | ||
| <h2>Further Reading</h2> | ||
| <ul className="list-disc mt-4 pl-6"> | ||
| {article.relatedLinks.map((item, index) => ( | ||
| <li key={index}> | ||
| <Link href={item.url} target="_blank" rel="noopener noreferrer" className="external-link"> | ||
| {item.title} | ||
| </Link> | ||
| </li> | ||
| ))} | ||
| </ul> | ||
| </div> | ||
| )} | ||
| </div> | ||
| ); | ||
| } | ||
97 changes: 97 additions & 0 deletions
97
frontend/src/app/(public-area)/articles/capital-one-data-breach.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| const article = { | ||
| title: 'Capital One Data Breach – A Cautionary Tale', | ||
| description: | ||
| 'Capital One lost hundreds of millions after being notified by a third party of an intruder that had been lurking in their AWS account for four months.', | ||
| date: new Date(Date.parse('2025-04-15T23:10:00-04:00')), | ||
| content: ( | ||
| <> | ||
| <p> | ||
| In July of 2019, Capital One Financial Corporation announced that they had recently become aware of a | ||
| data breach compromising the personal data of over 100 million customers in the United States, including | ||
| 140,000 Social Security numbers and 80,000 bank account numbers. Capital One estimated that the direct | ||
| financial impact of the incident would be between $100 million and $150 million for notifications, | ||
| credit monitoring, technology, and legal support. Furthermore, trust in the company had plummeted, and | ||
| within two weeks of the incident, Capital One's share price had dropped by 14%. | ||
| </p> | ||
| <p> | ||
| The impact was substantial, but it might have been much less severe if not for the fact that{' '} | ||
| <strong>the breach had occurred almost four months before it was discovered</strong>. Capital One had | ||
| been moving its operations to the cloud, and had failed to adequately manage the risk of doing so. This | ||
| mistake would later cost them another $80 million in fines when all was said and done. | ||
| </p> | ||
| <p> | ||
| So what should they have done to manage their risk better? Let's break down the incident in detail | ||
| so we can learn from their mistakes. | ||
| </p> | ||
| <h2>Foothold</h2> | ||
| <p> | ||
| On March 22, 2019, the attacker discovered that they could trick Capital One EC2 servers running in AWS | ||
| into executing arbitrary HTTP requests, enabling a type of attack known as Server Side Request Forgery | ||
| (SSRF). They were able to do this as a result of a misconfigured web application firewall (WAF) known as | ||
| ModSecurity. The WAF allowed the attacker to send requests to the AWS metadata service, which among | ||
| other things provides the EC2 servers with temporary credentials for the purpose of invoking AWS APIs. | ||
| </p> | ||
| <p> | ||
| At this point, the attacker could impersonate the EC2 servers and perform any action that the associated | ||
| IAM role allowed. | ||
| </p> | ||
| <h2>Lateral Movement</h2> | ||
| <p> | ||
| Once the attacker gained their foothold, they began to enumerate everything they had access to. Usually,{' '} | ||
| <strong>this is loud</strong> as attackers typically just have to try things and see what works. If you | ||
| were to look at this role in Cloud Snitch, you would see it attempting (and hopefully failing) to do a | ||
| lot of things that you would not expect a WAF to do. | ||
| </p> | ||
| <p> | ||
| In Capital One's case, the permissions attached to the role allowed the attacker to read and | ||
| decrypt sensitive data from Amazon S3 buckets. The attacker was able to exfiltrate all of this data via | ||
| the WAF's credentials, which again should have sounded alarm bells. | ||
| </p> | ||
| <h2>Discovery</h2> | ||
| <p> | ||
| Capital One had completely failed to detect the intrusion. They only became aware of it when a third | ||
| party notified them via their Responsible Disclosure Program on July 17, 2019. | ||
| </p> | ||
| <p> | ||
| Capital One was completely oblivious to the fact that there was an intruder in their cloud for almost | ||
| four months. The presence of the intruder would have made a lot of noise in their CloudTrail logs, but | ||
| without the proper tools, that log activity went completely unnoticed. This is why Cloud Snitch was | ||
| created. | ||
| </p> | ||
| <p> | ||
| <strong> | ||
| With Cloud Snitch, you can easily confirm that your AWS resources are doing what you expect them to | ||
| do and nothing more. | ||
| </strong> | ||
| </p> | ||
| </> | ||
| ), | ||
| relatedLinks: [ | ||
| { | ||
| title: 'Capital One Announces Data Security Incident', | ||
| url: 'https://investor.capitalone.com/news-releases/news-release-details/capital-one-announces-data-security-incident/', | ||
| }, | ||
| { | ||
| title: 'Information on the Capital One cyber incident', | ||
| url: 'https://www.capitalone.com/digital/facts2019/', | ||
| }, | ||
| { | ||
| title: 'A Systematic Analysis of the Capital One Data Breach: Critical Lessons Learned', | ||
| url: 'https://dl.acm.org/doi/10.1145/3546068', | ||
| }, | ||
| { | ||
| title: 'Capital One to pay $80 million fine after data breach', | ||
| url: 'https://www.reuters.com/article/business/capital-one-to-pay-80-million-fine-after-data-breach-idUSKCN2522D8/', | ||
| }, | ||
| { | ||
| title: 'Capital One Data Breach — 2019', | ||
| url: 'https://medium.com/nerd-for-tech/capital-one-data-breach-2019-f85a259eaa60', | ||
| }, | ||
| { | ||
| title: 'Lessons from the Capital One Breach on Cloud Security', | ||
| url: 'https://www.darktrace.com/blog/back-to-square-one-the-capital-one-breach-proved-we-must-rethink-cloud-security', | ||
| }, | ||
| ], | ||
| }; | ||
|
|
||
| export default article; |
121 changes: 0 additions & 121 deletions
121
frontend/src/app/(public-area)/articles/capital-one-data-breach/page.tsx
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| import CapitalOneDataBreach from './capital-one-data-breach'; | ||
|
|
||
| interface Article { | ||
| title: string; | ||
| description: string; | ||
| date: Date; | ||
| content: React.ReactNode; | ||
| relatedLinks?: Array<{ title: string; url: string }>; | ||
| } | ||
|
|
||
| export const articles: Record<string, Article> = { | ||
| 'capital-one-data-breach': CapitalOneDataBreach, | ||
| }; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import { ChevronRightIcon } from '@heroicons/react/24/outline'; | ||
| import type { Metadata } from 'next'; | ||
| import Link from 'next/link'; | ||
|
|
||
| import { articles } from '.'; | ||
|
|
||
| export const metadata: Metadata = { | ||
| title: 'Articles', | ||
| description: 'Articles and resources related to AWS, InfoSec, and Cloud Snitch.', | ||
| }; | ||
|
|
||
| const Page = () => { | ||
| const sortedArticles = Object.entries(articles).sort(([, a], [, b]) => b.date.getTime() - a.date.getTime()); | ||
|
|
||
| return ( | ||
| <div className="flex flex-col gap-4 [&_p]:my-4"> | ||
| <div className="translucent-snow p-4 rounded-lg"> | ||
| <h1>Articles</h1> | ||
| <div className="flex flex-col gap-4 mt-4"> | ||
| {sortedArticles.map(([slug, article]) => ( | ||
| <div className="border-1 border-platinum rounded-lg p-4" key={slug}> | ||
| <h2>{article.title}</h2> | ||
| <div className="text-sm text-english-violet"> | ||
| {article.date.toLocaleDateString('en-US', { | ||
| weekday: 'long', | ||
| year: 'numeric', | ||
| month: 'long', | ||
| day: 'numeric', | ||
| })} | ||
| </div> | ||
| <p>{article.description}</p> | ||
| <div className="flex"> | ||
| <Link | ||
| href={`/articles/${slug}`} | ||
| className="button flex grow-0 items-center whitespace-nowrap" | ||
| > | ||
| Read More | ||
| <ChevronRightIcon className="h-4 w-4 ml-2" /> | ||
| </Link> | ||
| </div> | ||
| </div> | ||
| ))} | ||
| </div> | ||
| </div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default Page; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.