
내 웹사이트를 내가 해킹해봤다 (OWASP Top 10과 보안의 기본)
고등학교 시절, 친구의 웹사이트를 'admin' --' 한 줄로 뚫었던 경험과 그 충격을 공유합니다. 웹 개발자가 반드시 알아야 할 10가지 보안 취약점(OWASP Top 10)과 이를 막기 위해 '해커처럼 생각하는 법'을 4가지 핵심 위협(Injection, IDOR, Crypto, Misconfig)을 중심으로 설명합니다.

고등학교 시절, 친구의 웹사이트를 'admin' --' 한 줄로 뚫었던 경험과 그 충격을 공유합니다. 웹 개발자가 반드시 알아야 할 10가지 보안 취약점(OWASP Top 10)과 이를 막기 위해 '해커처럼 생각하는 법'을 4가지 핵심 위협(Injection, IDOR, Crypto, Misconfig)을 중심으로 설명합니다.
프론트엔드 개발자가 알아야 할 4가지 저장소의 차이점과 보안 이슈(XSS, CSRF), 그리고 언제 무엇을 써야 하는지에 대한 명확한 기준.

Debug에선 잘 되는데 Release에서만 죽나요? 범인은 '난독화'입니다. R8의 원리, Mapping 파일 분석, 그리고 Reflection을 사용하는 라이브러리를 지켜내는 방법(@Keep)을 정리해봤습니다.

비트코인은 블록체인의 일부입니다. 이중 지불 문제(Double Spending), 작업 증명(PoW)과 지분 증명(PoS)의 차이, 스마트 컨트랙트, 그리고 Web 3.0이 가져올 미래까지. 개발자 관점에서 본 블록체인의 모든 것.

내 서버가 해킹당하지 않는 이유. 포트와 IP를 검사하는 '패킷 필터링'부터 AWS Security Group까지, 방화벽의 진화 과정.

고등학교 컴퓨터 동아리 시간이었습니다. 친구가 PHP로 공들여 만든 로그인 페이지를 자랑하더군요. "야, 이거 비밀번호 20자리로 설정했고, 특수문자도 넣었어. 절대 못 뚫어."
저는 웃으며 아이디 입력창에 이렇게 쳤습니다.
admin' --
비밀번호 칸은 비워둔 채 엔터를 쳤습니다. 결과는? "관리자님 환영합니다"
친구의 얼굴이 하얗게 질렸던 그 순간을 잊을 수 없습니다. 친구는 "어떻게 비밀번호도 없이 들어왔어?"라며 패닉에 빠졌습니다. 이것이 제 인생 첫 해킹이자, SQL Injection과의 첫 만남이었습니다.
그날 저는 깨달았습니다. 보안은 "강력한 자물쇠(비밀번호)"를 다는 게 아니라, "문틀(코드)" 자체가 튼튼해야 한다는 것을요.
웹 보안은 방대하지만, 다행히 전 세계 보안 전문가들이 "가장 위험한 웹 취약점 10개"를 모아놓은 리스트가 있습니다. 바로 OWASP (Open Web Application Security Project) Top 10입니다.
이 리스트는 몇 년마다 갱신되는데, 해커들이 가장 애용하는 공격 루트이자 개발자들이 가장 실수하기 쉬운 부분들을 모아놓은 것입니다. 이 10가지만 확실히 이해하고 막아도, 여러분 사이트 보안의 90%는 해결됩니다.
오늘날 가장 흔하게 발견되는 핵심 취약점 4가지를 깊이 있게 파헤쳐 봤습니다.
옛날엔 Injection이 1위였지만, 지금은 이게 1위입니다. 가장 흔하고 막기 어렵기 때문입니다. 흔히 IDOR (Insecure Direct Object References)라고도 불립니다.
/orders/100이네? 그럼 /orders/101로 바꾸면 다른 사람 주문도 보일까?"// DB에서 그냥 ID로 조회해서 바로 줌 (권한 검사 없음)
app.get('/orders/:id', (req, res) => {
const order = db.findOrder(req.params.id);
res.json(order);
});
app.get('/orders/:id', (req, res) => {
const order = db.findOrder(req.params.id);
// 핵심: 주문자가 현재 로그인한 유저와 같은지 확인
if (order.userId !== req.session.userId) {
return res.status(403).send("Forbidden");
}
res.json(order);
});
주의: 프론트엔드에서 버튼을
hidden으로 숨기는 건 보안이 아닙니다. API 레벨에서 막아야 합니다.
민감한 뎅터를 평문(Plain Text)으로 저장하거나, 약한 암호화 알고리즘을 쓰는 경우입니다.
users 테이블을 열어보니 비밀번호가 password123 그대로 보입니다. 해커는 룰루랄라 이 비밀번호로 다른 사이트들(네이버, 구글)도 로그인해 봅니다. (사람들은 비밀번호를 재사용하니까요)base64로 인코딩함 (인코딩은 암호화가 아닙니다! 누구나 풀 수 있어요.)MD5, SHA-1 같은 낡은 해시 함수 사용.제 친구를 울렸던 바로 그 녀석입니다. 사용자의 입력을 코드의 일부로 실행해버리는 취약점입니다.
SELECT * FROM users WHERE id = '$inputId';
여기에 $inputId로 ' OR '1'='1을 넣으면?
SELECT * FROM users WHERE id = '' OR '1'='1';
1=1은 항상 참(True)이므로, DB의 모든 사용자 정보를 뱉어냅니다.// Good: 입력값을 ? 로 치환해서 받음 (컴파일러가 코드로 인식 안 함)
db.query('SELECT * FROM users WHERE id = ?', [inputId]);
코드는 완벽한데 설정 때문에 뚫리는 경우입니다.
admin / password)를 안 바꿈.Public으로 열어둠.보안을 잘하려면 방어자가 아니라 공격자의 입장에서 생각해야 합니다. 이를 "Offensive Security"라고 합니다.
<script>)를 넣으면 실행될까?" (XSS)-10000원을 넣으면 내 통장에 돈이 들어올까?" (Business Logic Error)개발자는 기능을 "동작하게" 만드는 사람이지만, 보안 전문가는 기능을 "고장 나게" 만드는 사람입니다. 이 두 가지 시각을 모두 가져야 비로소 '시니어 개발자'라고 할 수 있습니다.
"에이, 누가 우리 같은 작은 사이트를 해킹하겠어? 가져갈 정보도 없는데." 가장 위험한 생각입니다.
해커들은 일일이 손으로 타겟을 정하고 해킹하지 않습니다.
자동화된 봇(Bot)이 24시간 내내 전 세계의 IP를 스캔하며 admin.php, wp-login.php, .env 파일을 찾고 다닙니다.
취약점이 발견되면 그 즉시 뚫리는 겁니다.
구멍이 있으면, 뚫리는 건 if가 아니라 when의 문제입니다.
지금 당장 여러분의 코드를 돌아보세요. admin' -- 한 줄에 무너지는 성은 아닌지.
It was a high school computer club meeting. My friend was proudly showing off a login page he built with PHP. "Hey, look at this. I set the password to 20 characters with special symbols. You'll never crack it."
I smiled, walked up to his keyboard, and typed this into the ID field:
admin' --
I left the password field empty and hit Enter. The result? "Welcome, Admin."
I'll never forget the look of absolute horror on his face. "How... how did you get in without the password?" This was my first hack, and my first encounter with SQL Injection.
That day, I learned a valuable lesson: Security isn't about putting a strong lock (User Password) on the door. It's about making sure the doorframe (Code) itself doesn't fall off.
Web security is vast, but thankfully, security experts worldwide have compiled a list of the "10 Most Dangerous Web Vulnerabilities." It's called the OWASP (Open Web Application Security Project) Top 10.
This list is updated every few years. It represents the vulnerabilities that hackers love the most and developers implement poorly the most often. If you understand and mitigate just these 10, you solve 90% of your site's security issues.
Let's dive deep into the top 4 vulnerabilities that are most relevant today.
Formerly lower on the list, this is now #1. Why? Because it's common and hard to automate detection for. Often called IDOR (Insecure Direct Object References).
/orders/100. If I change it to /orders/101, can I see someone else's order?"// Vulnerable: Fetches order by ID without checking who owns it
app.get('/orders/:id', (req, res) => {
const order = db.findOrder(req.params.id);
res.json(order);
});
// Secure: Checks ownership
app.get('/orders/:id', (req, res) => {
const order = db.findOrder(req.params.id);
if (order.userId !== req.session.userId) {
return res.status(403).send("Forbidden");
}
res.json(order);
});
Note: Hiding delete buttons on the frontend with CSS is NOT security. Hackers use Postman, not Chrome.
Storing sensitive data in plain text or using weak encryption.
password123, the game is over. Hackers will try those email/password combos on other sites (Credential Stuffing).Base64. (Encoding is NOT encryption. It's reversible.)MD5 or SHA-1.The classic vulnerability that made my friend cry. It occurs when untrusted user input is interpreted as code.
SELECT * FROM users WHERE id = '$inputId';
Input: ' OR '1'='1
Result: SELECT * FROM users WHERE id = '' OR '1'='1';
Since 1=1 is always true, the database returns every single user record.// Good: Input remains data, not executable SQL
db.query('SELECT * FROM users WHERE id = ?', [inputId]);
The code is perfect, but the setup is flawed.
admin / admin).To be a good defender, you must think like an attacker. This mindset shift is what separates junior devs from seniors.
<script>alert(1)</script> in the username field, will it execute?" (XSS)-100 dollars to my friend, will 100 dollars be subtracted from him and added to me?" (Logic Error)Developers are trained to make features limitlessly functional. Security experts are trained to find where those functions break limitlessly. You need to wear both hats.
Modern frameworks protect you from a lot (like XSS via automatic escaping), but they are not silver bullets. They cannot protect you from Logic Errors (like Broken Access Control) or Misconfigurations. You still need to code securely.
You don't have to check everything manually.
npm audit or Snyk check if your dependencies (node_modules) have known vulnerabilities.Ideally, continuously. Integrate security tools into your CI/CD pipeline (DevSecOps). Security shouldn't be a "once a year" event; it should be part of every commit.
You don't need to be a hacker to test your site. Use these free tools:
"Who would hack our small startup? We have nothing to steal." This is the most dangerous assumption.
Hackers don't sit there manually typing URLs. They use Automated Bots that scan millions of IPs 24/7, looking for known vulnerabilities like open .env files or outdated WordPress plugins.
If you have a hole, you will be found. It's not a matter of if, but when.
Secure your code today. Don't let a single quote ' be the reason your startup fails.
You don't need to be a hacker to test your site. Use these free tools: