~/posts/technology/warum-wir-cvd-policy-entwickeln.md

Warum wir CVD-Policy entwickelnWhy We Are Developing CVD-Policy

·technology·
#cvd#ietf#open-source#security

Ben Luca Behring und Marco Berg · Skalvar Technologies

Am 1. September haben wir draft-behring-cvd-policy-00 veröffentlicht: einen Internet-Draft für maschinenlesbare Coordinated-Vulnerability-Disclosure-Policies. Der Entwurf beschreibt ein JSON-Format und dessen Einbindung über security.txt. Er ist ein Vorschlag, kein verabschiedeter Standard. (ietf.org)

Der Ausgangspunkt ist eine Frage, die uns bei der Arbeit an unserem CVD-Service Aeskal beschäftigt: Wie kann Software die Disclosure-Policy einer Organisation auswerten, ohne deren Bedeutung selbst erraten zu müssen?

Wir möchten beispielsweise feststellen können, für welche Systeme eine Organisation Meldungen entgegennimmt, welche Aussagen sie zu Sicherheitsuntersuchungen macht und welche Bedingungen dabei gelten sollen. Diese Informationen sollen unabhängig davon nutzbar sein, wer die Policy veröffentlicht oder welches Werkzeug sie verarbeitet.

Die JSON-Datei ist dabei der überschaubare Teil. Interessanter ist, was ein Programm aus ihrem Inhalt schließen darf.

Was hinter dem Link steht

security.txt löst bereits einen wichtigen Teil des Problems. Eine Organisation kann unter /.well-known/security.txt Kontaktmöglichkeiten veröffentlichen und mit dem Feld Policy auf ihre Disclosure-Policy verweisen. Die Datei ist selbst maschinenlesbar; wir müssen diesen Mechanismus nicht noch einmal erfinden. (RFC-Editor)

Uns interessiert der nächste Schritt: die Bedeutung dessen, was hinter dem Policy-Link steht.

Nehmen wir eine Organisation, die auf ihrer Website schreibt:

Wir begrüßen Meldungen zu Sicherheitsproblemen in unserer API.

Das ist eine hilfreiche Aussage. Sie beantwortet aber nicht automatisch die Frage, ob jemand einen automatisierten Scanner gegen diese API laufen lassen soll. Vielleicht möchte die Organisation ausschließlich bereits entdeckte Probleme entgegennehmen. Vielleicht begrüßt sie Tests, erwartet aber eine begrenzte Anfragerate. Vielleicht gibt es einzelne Endpunkte, die ausdrücklich ausgenommen sind.

Ein Mensch kann den weiteren Text lesen, Zusammenhänge berücksichtigen und nachfragen. Ein Werkzeug braucht für eine reproduzierbare Auswertung konkretere Angaben.

Natürlich könnte jede Anwendung dafür ein eigenes Datenmodell verwenden. Für Aeskal könnten wir das ebenfalls tun. Wir halten es aber für wenig sinnvoll, wenn die Bedeutung einer veröffentlichten Policy davon abhängt, welches Produkt sie gerade einliest.

CVD-Policy ist unser Versuch, diesen gemeinsamen Teil zu beschreiben.

Die Einbindung soll langweilig bleiben

Eine Organisation soll dafür keine neue Anwendung betreiben müssen. Unser bevorzugter Ausgangspunkt sind zwei veröffentlichte Dateien: die vorhandene security.txt und ein JSON-Dokument.

Ein Beispiel für die vorgeschlagene Einbindung:

Contact: mailto:security@example.com
Expires: 2027-03-01T00:00:00Z
Policy: https://example.com/security/disclosure
CVD-Policy: https://example.com/security/cvd-policy.json

Das vorgeschlagene Feld CVD-Policy enthält genau eine absolute HTTPS-Adresse. Der Client folgt dieser Adresse; er rät keinen Pfad wie /.well-known/cvd.json. Die menschenlesbare Policy behält ihren eigenen Verweis. (ietf.org)

Ein fester Dateiname wäre bequem. Wir bevorzugen hier trotzdem einen ausdrücklichen Verweis. Damit muss die Frage, wo eine Organisation ihre Policy bereitstellt, nicht gleichzeitig durch das Datenformat beantwortet werden.

Diese Entscheidung hat einen kleinen Preis: Es gibt eine zusätzliche Verknüpfung, die eingerichtet und gepflegt werden muss. Für uns ist das vertretbar. Der Einstieg bleibt eindeutig, während die Bereitstellung flexibel bleibt.

Wir möchten außerdem nicht verlangen, dass ein verständlicher Policy-Text zugunsten von JSON verschwindet. Menschen brauchen weiterhin Erklärungen, Beispiele und Ansprechpartner. Eine strukturierte Fassung soll die Angaben ergänzen, die ein Programm zuverlässig verarbeiten können muss.

Meldungen annehmen und Tests gestatten sind unterschiedliche Aussagen

Das ist für uns die wichtigste Unterscheidung im Entwurf.

CVD-Policy trennt den Geltungsbereich für Meldungen von konkreten Testregeln. Ein aufgeführtes System begründet allein keine positive Aussage zu Tests. Auch eine grundsätzlich offene Haltung gegenüber Sicherheitsforschung ersetzt keine passende Regel. (ietf.org)

Warum legen wir darauf so viel Wert?

Weil es für eine Anwendung verlockend wäre, diese Informationen auf eine einfache Entscheidung zu reduzieren: System gefunden, Organisation offen für Meldungen, also grünes Häkchen anzeigen.

Dieses Häkchen würde allerdings mehr behaupten, als die zugrunde liegenden Angaben hergeben.

Der positive Auswertungsstatus heißt deshalb publisher-stated-permitted. Er beschreibt eine Aussage des Herausgebers, keine rechtliche Erlaubnis und keinen garantierten Safe Harbor. Fehlende Regeln oder unverstandene kritische Erweiterungen dürfen kein positives Ergebnis erzeugen. (ietf.org)

Der Name ist länger als allowed. Wir halten diese zusätzliche Genauigkeit für sinnvoll. Ein kurzer Bezeichner hilft wenig, wenn seine naheliegende Interpretation falsch ist.

Dasselbe gilt für die Herkunft einer Policy. Die Zuordnung zum Zielhost entsteht über dessen geprüfte security.txt, nicht dadurch, dass eine JSON-Datei ihn nennt. Subdomains erben diese Zuordnung nicht automatisch. (ietf.org)

Das lässt sich an einem einfachen Gedankenexperiment prüfen: Wir könnten auf unserem Server ein Dokument veröffentlichen, das beliebige fremde Systeme auflistet. Ein Werkzeug dürfte daraus offensichtlich keine entsprechende Aussage dieser Systeme ableiten.

Solche Fälle sind für uns der eigentliche Grund, eine Spezifikation zu schreiben. Die Feldnamen allein reichen nicht. Wir müssen auch festhalten, welche Schlussfolgerungen zulässig sind und wann die vorhandenen Informationen keine Entscheidung tragen.

Das Format muss ohne Aeskal funktionieren

Wir entwickeln Aeskal, und wir möchten CVD-Policy dort verwenden. Daraus soll aber keine Abhängigkeit in die andere Richtung entstehen.

Unser Maßstab ist einfach: Eine Organisation soll die Policy selbst bereitstellen und ein anderes Werkzeug verwenden können, ohne dafür mit uns sprechen zu müssen. Auch ein konkurrierender Dienst soll das Format implementieren können.

Die Projektspezifikation, das Schema und der Testkorpus sind unter CC0-1.0 veröffentlicht. Die Referenzimplementierung und der Website-Code stehen unter Apache-2.0. Das Projekt wird derzeit von Skalvar Technologies gepflegt. (GitHub)

Für uns bedeutet Offenheit hier mehr als ein öffentlich lesbares Repository. Entscheidend ist, ob jemand unsere Implementierung ersetzen kann.

Das beeinflusst auch den Umfang. Die V1-Referenzimplementierung enthält keine API zur Übermittlung oder Annahme von Schwachstellenmeldungen. (GitHub) Das halten wir für eine hilfreiche Grenze: Wie ein Dienst Meldungen entgegennimmt, Anhänge verarbeitet oder Fälle organisiert, muss nicht durch dasselbe Format festgelegt werden, das die veröffentlichte Policy beschreibt.

Für Aeskal brauchen wir diese Funktionen natürlich trotzdem. Wir müssen sie nur nicht zu Voraussetzungen für alle anderen machen.

Was wir mit dem Entwurf prüfen wollen

Neben dem Text gibt es ein Schema, Beispiele, einen Testkorpus und eine experimentelle V1-Referenzimplementierung. Deren API ist unter @cvd-policy/core/v1 von den älteren Schnittstellen getrennt. (GitHub)

Damit lässt sich bereits konkret über Verhalten sprechen. Eine Policy kann gültig oder ungültig sein. Eine Auswertung kann ein erwartetes oder ein unerwartetes Ergebnis liefern. Ein Fehler lässt sich auf eine Eingabe zurückführen, statt nur auf unterschiedliche Vorstellungen davon, was ein Absatz eigentlich sagen sollte.

Wir wollen die Aussagekraft unserer eigenen Implementierung dabei nicht überschätzen.

Wenn wir eine Spezifikation schreiben und anschließend Code entwickeln, der unserem Verständnis dieser Spezifikation entspricht, zeigt das zunächst vor allem, dass unser Text und unser Code zusammenpassen. Es zeigt noch nicht, dass jemand ohne unseren Entwicklungskontext zum selben Ergebnis kommt.

Genau das interessiert uns jetzt.

Kann eine andere Organisation ihre tatsächliche Policy damit ausdrücken? Versteht ein unabhängiger Entwickler die Auswertungsregeln genauso wie wir? Gibt es eine Bedingung, die in der Praxis wichtig ist, aber im Modell keinen sinnvollen Platz findet? Oder haben wir etwas kompliziert gemacht, das einfacher sein könnte?

Am hilfreichsten sind dafür konkrete Gegenbeispiele: eine Richtlinie, die sich nicht abbilden lässt, eine mehrdeutige Formulierung oder zwei Implementierungen, die bei derselben Eingabe unterschiedliche Ergebnisse liefern.

Wir haben den Draft veröffentlicht, um diese Fragen außerhalb unserer eigenen Implementierung prüfen zu können. Unser Ziel ist ein Format, dessen Bedeutung nicht erklärt werden muss, indem man die Autoren fragt.

Ben Luca Behring and Marco Berg · Skalvar Technologies

On September 1, we published draft-behring-cvd-policy-00: an Internet-Draft for machine-readable coordinated vulnerability disclosure policies. The draft describes a JSON format and how it is linked through security.txt. It is a proposal, not an adopted standard. (ietf.org)

Our starting point is a question that has occupied us while working on our CVD service Aeskal: How can software evaluate an organization's disclosure policy without having to guess what it means?

For example, we want to determine which systems an organization accepts reports for, what statements it makes about security research, and which conditions should apply. This information should be usable regardless of who publishes the policy or which tool processes it.

The JSON file is the manageable part. What is more interesting is what a program may infer from its contents.

What Lies Behind the Link

security.txt already solves an important part of the problem. An organization can publish contact details at /.well-known/security.txt and use the Policy field to link to its disclosure policy. The file itself is machine-readable; we do not need to reinvent that mechanism. (RFC Editor)

We are interested in the next step: the meaning of what lies behind the policy link.

Consider an organization that writes on its website:

We welcome reports of security issues in our API.

That is a useful statement. But it does not automatically answer whether someone should run an automated scanner against this API. The organization may only want to receive reports about problems that have already been discovered. It may welcome testing but expect a limited request rate. Or specific endpoints may be explicitly excluded.

A person can read the rest of the text, consider the context, and ask questions. A tool needs more specific information for reproducible evaluation.

Of course, every application could use its own data model for this. We could do the same for Aeskal. But we do not think it makes much sense for the meaning of a published policy to depend on which product happens to read it.

CVD-Policy is our attempt to describe this shared part.

Keeping the Integration Boring

An organization should not have to operate a new application for this. Our preferred starting point is two published files: the existing security.txt and a JSON document.

An example of the proposed integration:

Contact: mailto:security@example.com
Expires: 2027-03-01T00:00:00Z
Policy: https://example.com/security/disclosure
CVD-Policy: https://example.com/security/cvd-policy.json

The proposed CVD-Policy field contains exactly one absolute HTTPS address. The client follows that address; it does not guess a path such as /.well-known/cvd.json. The human-readable policy keeps its own link. (ietf.org)

A fixed filename would be convenient. Even so, we prefer an explicit link here. This means that the question of where an organization provides its policy does not also have to be answered by the data format.

This decision comes at a small cost: one additional link has to be configured and maintained. We consider that acceptable. The entry point remains unambiguous while hosting remains flexible.

We also do not want to require understandable policy text to disappear in favor of JSON. People still need explanations, examples, and contacts. A structured version should supplement the information that a program must be able to process reliably.

Accepting Reports and Permitting Tests Are Different Statements

For us, this is the most important distinction in the draft.

CVD-Policy separates the scope for reports from specific testing rules. Listing a system alone does not establish a positive statement about testing. A generally open attitude toward security research does not replace an applicable rule either. (ietf.org)

Why do we place so much emphasis on this?

Because an application would be tempted to reduce this information to a simple decision: system found, organization open to reports, so display a green check mark.

That check mark, however, would claim more than the underlying information supports.

The positive evaluation status is therefore called publisher-stated-permitted. It describes a statement by the publisher, not legal permission or guaranteed safe harbor. Missing rules or unrecognized critical extensions must not produce a positive result. (ietf.org)

The name is longer than allowed. We think that additional precision is worthwhile. A short identifier is of little help if its obvious interpretation is wrong.

The same applies to a policy's origin. Its association with the target host is established through that host's verified security.txt, not because a JSON file names it. Subdomains do not inherit this association automatically. (ietf.org)

A simple thought experiment demonstrates why: We could publish a document on our server that lists any number of third-party systems. A tool clearly must not infer corresponding statements from those systems.

Cases like these are the real reason we are writing a specification. Field names alone are not enough. We also need to define which conclusions are valid and when the available information does not support a decision.

The Format Must Work Without Aeskal

We are developing Aeskal, and we want to use CVD-Policy there. But that must not create a dependency in the opposite direction.

Our standard is simple: An organization should be able to host the policy itself and use another tool without having to speak to us. A competing service should also be able to implement the format.

The project specification, schema, and test corpus are published under CC0-1.0. The reference implementation and website code are licensed under Apache-2.0. The project is currently maintained by Skalvar Technologies. (GitHub)

For us, openness here means more than a publicly readable repository. What matters is whether someone can replace our implementation.

This also shapes the scope. The V1 reference implementation does not include an API for submitting or accepting vulnerability reports. (GitHub) We consider that a useful boundary: How a service receives reports, processes attachments, or organizes cases does not have to be defined by the same format that describes the published policy.

Aeskal still needs these functions, of course. We just do not need to make them prerequisites for everyone else.

What We Want to Test with the Draft

In addition to the text, there is a schema, examples, a test corpus, and an experimental V1 reference implementation. Its API is separated from the older interfaces under @cvd-policy/core/v1. (GitHub)

This already lets us discuss behavior concretely. A policy can be valid or invalid. An evaluation can produce an expected or unexpected result. An error can be traced to an input instead of merely to different ideas about what a paragraph was supposed to mean.

We do not want to overstate what our own implementation demonstrates.

When we write a specification and then develop code that matches our understanding of that specification, it initially shows above all that our text and our code agree. It does not yet show that someone without our development context will reach the same result.

That is exactly what interests us now.

Can another organization express its actual policy with this format? Does an independent developer understand the evaluation rules the same way we do? Is there a condition that matters in practice but has no sensible place in the model? Or have we made something complicated that could be simpler?

The most helpful feedback consists of concrete counterexamples: a policy that cannot be represented, ambiguous wording, or two implementations that produce different results for the same input.

We published the draft to test these questions outside our own implementation. Our goal is a format whose meaning does not have to be explained by asking its authors.