Sind GUID-Kollisionen möglich?


128

Ich arbeite an einer Datenbank in SQL Server 2000, die eine GUID für jeden Benutzer verwendet, der die App verwendet, an die er gebunden ist. Irgendwie hatten zwei Benutzer dieselbe GUID. Ich weiß, dass Microsoft einen Algorithmus verwendet, um eine zufällige GUID zu generieren, die eine äußerst geringe Wahrscheinlichkeit hat, Kollisionen zu verursachen. Ist eine Kollision dennoch möglich?


11
Jeder, der Nein sagt, ist falsch. Ich habe bereits 1 UniqueIdentifier mit einem Datensatz von weniger als einer halben Million Datensätzen kollidiert, MSSQL 2008 R2
Behrooz

2
@Behrooz Yikes. Dank unseres Freundes ist das Geburtstagsparadox nicht unmöglich, aber es ist immer noch wahnsinnig unglücklich mit völlig zufälligen v4-GUIDs. Vielleicht haben Sie eine schwächere Strategie zur GUID-Generierung verwendet?
Craig Ringer

6
@Behrooz Wow. Das ist schockierendes Glück.
Craig Ringer

6
@Behrooz Dies ist wahrscheinlich eine fehlerhafte Pseudozufallszahl, die in MSSQL verwendet wird (ich wäre nicht überrascht, wenn sie 32-Bit-Seed in ihrem Generator oder dergleichen haben, angesichts der Qualität ihrer Software). Die Mathematik lügt nicht. Diese Möglichkeit ist so gering, dass Sie 99,9999999999 (und viele 9 nach)% sein können, dass entweder der MSSQL-Guid-Generator defekt ist (oder ein Pseudozufallsgenerator ist, der zum Generieren von GUIDs verwendet wird) oder Sie einen Fehler gemacht haben.
Alex

2
Ich liebe es, wie genau in diesem Moment sowohl die Frage als auch die ausgewählte Antwort 128 Punkte haben. Zufall? 🤔
Caio Cunha

Antworten:


128

Grundsätzlich nein. Ich glaube, jemand hat sich mit Ihrer Datenbank beschäftigt. Abhängig von der von Ihnen verwendeten Versions-GUID ist der Wert entweder eindeutig (für GUIDs der Version 1) oder sowohl eindeutig als auch unvorhersehbar (für GUIDs der Version 4). Die Implementierung von SQL Server für die Funktion NEWID () scheint eine 128-Bit-Zufallszahl zu verwenden, sodass keine Kollision auftritt.

Für eine Kollisionswahrscheinlichkeit von 1% müssten Sie ungefähr 2.600.000.000.000.000.000 GUIDs generieren .


3
Das habe ich mir gedacht, aber ich wollte nur sicherstellen, dass ich das nicht ausschließen kann. Sie wissen nie, welche seltsamen Fehler in 8 Jahre alter Software auftreten können. :)
Jason Baker

6
Eigentlich stimmt das nicht mehr. Dies galt für v1-GUIDs, nicht jedoch für die aktuellen v4-GUIDs. Weitere Informationen finden Sie unter en.wikipedia.org/wiki/Globally_Unique_Identifier#Algorithm .
Greg Beech

94
Down-Voting, weil Sie im Prinzip (in seiner gröbsten Form) zu Unrecht "Nein" zu der Frage "Sind GUID-Kollisionen möglich?" Sagen. Es ist sehr gut möglich. Die Wahrscheinlichkeit dafür ist gering, aber es ist möglich. Ich mag es nicht, pedantisch zu klingen - aber bei SO geht es darum, präzise und genau zu sein.

13
Geben Sie "lösen [1-exp [- (n ^ 2 / (2 * 2 ^ 128))]> 0,01, n]" in wolfram alpha ein, um das Ergebnis für 1% zu erhalten ... Beachten Sie, dass diese Zahl zwar groß erscheint Kontext einer Anwendung, es ist sicherlich nicht groß für die ganze Welt. Wenn jeder Computer auf der Erde echte GUIDs erzeugen würde, würden sie innerhalb von etwa einer Sekunde eine Kollision mit einer Wahrscheinlichkeit von 1% verursachen, vorausgesetzt, sie könnten jede Nanosekunde eine GUID erzeugen (was heutzutage wahrscheinlich ziemlich realistisch ist). Wenn Sie also GUIDs für Ihre Datenbank-IDs verwenden, sind diese eindeutig. GUIDs für jede auf der Erde durchgeführte Berechnung kollidieren sofort.
Heilige

10
Wenn Sie "Nein" sagen, ist dies nicht möglich, und wenn Sie dann sagen, dass die Wahrscheinlichkeit einer Kollision von 1% besteht, wenn ein bestimmter Betrag generiert wird, handelt es sich um direkte Konflikte. Die richtige Antwort sollte theoretisch sein - ja, eine Kollision könnte zufällig auftreten. Die Wahrscheinlichkeit einer Kollision ist jedoch statistisch gesehen geringer als bei einem Asteroiden, der auf die Erde trifft, von der Erde abprallt und vom Mond abprallt, um die Erde in der nächsten Stunde ein zweites Mal zu treffen.
Baaleos

112

Grundsätzlich sind sie nicht möglich! sind die Chancen astronomisch gering .

Aber ... ich bin die einzige Person auf der Welt, die ich kenne und die einmal eine GUID-Kollision hatte (yep!).

Und ich bin mir sicher, dass es kein Fehler war.

Wie kam es, dass in einer kleinen Anwendung, die auf einem Pocket PC ausgeführt wurde, am Ende eines Vorgangs ein Befehl mit einer generierten GUID ausgegeben werden muss? Der Befehl wurde nach seiner Ausführung auf dem Server zusammen mit dem Ausführungsdatum in einer Befehlstabelle auf dem Server gespeichert. Eines Tages, als ich debuggte, gab ich den Modulbefehl aus (mit der neu generierten GUID) und nichts passierte. Ich habe es erneut getan (mit derselben Guid, da die Guid zu Beginn der Operation nur einmal generiert wurde) und erneut und nichts, um schließlich herauszufinden, warum der Befehl nicht ausgeführt wird, habe ich die Befehlstabelle überprüft. und die gleiche GUID wie die aktuelle wurde vor 3 Wochen eingefügt. Da ich das nicht glaubte, stellte ich eine Datenbank aus einer zweiwöchigen Sicherung wieder her, und die Anleitung war da. Überprüfte den Code, der neue Guid wurde ohne Zweifel frisch generiert.

Bearbeiten: Es gibt einige Faktoren, die die Wahrscheinlichkeit, dass dies geschieht, erheblich erhöht haben könnten. Die Anwendung wurde auf dem PocketPC-Emulator ausgeführt, und der Emulator verfügt über eine Funktion zum Speichern des Status. Dies bedeutet, dass bei jeder Wiederherstellung des Status auch die Ortszeit wiederhergestellt wird und die Guid basiert auf dem internen Timer .... auch der Guid-Generierungsalgorithmus für ein kompaktes Framework ist möglicherweise weniger vollständig als zum Beispiel der COM-Algorithmus ...


38
Upvoted. Status speichern und wiedergeben würde wirklich doppelte Guids erzeugen.
Joshua

35
Wahrscheinlich war dies eine "schlechte" GUID-Implementierung. Die theoretischen Chancen waren sehr gering, aber auf Pocket PC? Wer soll sagen, dass sie keine Abkürzung genommen haben, die diese Chancen in die Kategorie "unwahrscheinlich, aber möglich" gebracht hat?
Dave Dopson

9
Nur weil etwas mit einer sehr geringen Wahrscheinlichkeit passiert, heißt das nicht, dass es nicht passieren wird.
Renan

3
Wie ich oben sagte, sind die Chancen dafür so gering, dass man davon ausgehen kann, dass entweder Sie einen Fehler gemacht haben oder MSSQL ein defektes PRNG verwendet ( en.wikipedia.org/wiki/Pseudorandom_number_generator ). Beispielsweise ist es wahrscheinlich, dass dieses PRNG mit einem Samen kleiner Größe initialisiert wird. Defekte PRNGs sind nicht selten (siehe schneier.com/paper-prngs.html ) - beispielsweise wurde kürzlich ein Defekt im Android SDK entdeckt - android-developers.blogspot.com/2013/08/… + usenix.org/conference/woot14 / Workshop-Programm / Präsentation /…
Alex

2
@Alex, der Fehler war "Status speichern und wiederherstellen" vom Emulator, wodurch das gesamte Emulator-Image einschließlich der Emulator-Uhr wiederhergestellt wird. Nach Tausenden von Wiederherstellungsvorgängen über ein Jahr wurde eine Guid-Kollision generiert. Sie haben Recht, es war ein Fehler!
Pop Catalin

34

Sie sind theoretisch möglich, aber mit 3.4E38 möglichen Zahlen beträgt die Wahrscheinlichkeit, ein Duplikat zu erhalten, 0,00000000006 ( Quelle ) , wenn Sie in einem Jahr mehrere zehn Billionen GUIDs erstellen .

Wenn zwei Benutzer dieselbe GUID hätten, würde ich wetten, dass das Programm einen Fehler enthält, der dazu führt, dass die Daten kopiert oder gemeinsam genutzt werden.


"aber mit 3.4E38 möglichen Nummern" - nein. Zwei GUIDs, die fast gleichzeitig auf demselben Computer generiert werden, würden extrem ähnliche GUIDs ergeben.
Kirk Strauser

4
Dies hängt davon ab, wie die GUID generiert wird, und einige Implementierungen, die auf der CPU-Zeit oder den Millisekunden basieren, übertreiben (hoffentlich) die Berechnung, auf der zwei GUIDs basieren, die im Abstand von Millisekunden generiert wurden, haben einen großen Unterschied.
Dalin Seivewright

4
Wenn bei mehr als einem Prozessor auf einem Computer eine Guid auf Zeit und Mac-Adresse basiert, kann jeder Kern zum gleichen Zeitpunkt dieselbe Guid ausgeben.
AndyM

12
Ich bin mir ziemlich sicher, dass eine anständige GUID-Implementierung dies nicht tun wird
Guillaume86

1
@MatthewLock Das Geburtstagsparadoxon wird in der Quelle behandelt. Überprüfen Sie den Link.
Zero3

21

Betrachten wir zunächst die Wahrscheinlichkeit einer Kollision zweier GUIDs. Wie aus anderen Antworten hervorgeht, ist es aufgrund des Geburtstagsparadoxons nicht 1 zu 2 ^ 128 (10 ^ 38) , was bedeutet, dass bei einer 50% igen Wahrscheinlichkeit, dass zwei GUIDs kollidieren, die Wahrscheinlichkeit tatsächlich 1 zu 2 ^ 64 (10 ^) beträgt 19) was viel kleiner ist. Dies ist jedoch immer noch eine sehr große Zahl, und daher ist die Wahrscheinlichkeit einer Kollision unter der Annahme, dass Sie eine angemessene Anzahl von GUIDs verwenden, gering.

Beachten Sie auch, dass GUIDs keinen Zeitstempel oder die MAC-Adresse enthalten, wie viele Leute auch zu glauben scheinen. Dies galt für v1-GUIDs, aber jetzt werden v4-GUIDs verwendet, bei denen es sich einfach um eine Pseudozufallszahl handelt. Dies bedeutet, dass die Wahrscheinlichkeit einer Kollision wahrscheinlich höher ist, da sie nicht mehr nur für eine Zeit und eine Maschine gelten.

Die Antwort lautet also im Wesentlichen: Ja, Kollisionen sind möglich. Aber sie sind höchst unwahrscheinlich.

Bearbeiten: behoben, um 2 ^ 64 zu sagen


2
Obwohl ich all Ihren Fakten zustimme, seien Sie vorsichtig mit Ihrer Mathematik. Zu sagen, dass Sie eine 1: 10 ^ 19-Chance haben, dass zwei GUIDs kollidieren, hängt davon ab, wie viele GUIDs im Set enthalten sind. Für diese Chance benötigen Sie ~ 2 ^ 32 GUIDs, sodass die Chancen in fast allen realen Szenarien viel geringer sind.
DocMax

1
Sie haben einen Tippfehler, von 1 in 10^64 (10^19)dem ich denke, dass er sein sollte 1 in 2^64 (10^19). Ich bin auch sehr verwirrt, wie Sie denken, dass das Geburtstagsparadoxon nur für 2 Zahlen gilt. Ich nehme an, Sie haben sich en.wikipedia.org/wiki/Birthday_paradox angesehen . Die Tabelle zeigt, wie viele Hilfslinien Sie für eine bestimmte Wahrscheinlichkeit eines Duplikats benötigen. Aus dieser Tabelle ergibt eine Wahrscheinlichkeit von 1 zu 10 ^ 18 2,6 * 10 ^ 10 Guids, nicht etwas, das nur zwei GUIDs nahe kommt.
Tony Lee

Ein Punkt - v1-Guids sind immer noch weit verbreitet und basieren auf der MAC-Adresse, insbesondere in Datenbanken, da sie wünschenswerte Eigenschaften aufweisen. Siehe UuidCreateSequential und den SQL Server-Wrapper NewSequentialID ( msdn.microsoft.com/en-us/library/windows/desktop/… ).
EBarr

18

Die Wahrscheinlichkeit, dass zwei zufällige GUIDs kollidieren (~ 1 in 10 ^ 38), ist geringer als die Wahrscheinlichkeit, dass ein beschädigtes TCP / IP-Paket nicht erkannt wird (~ 1 in 10 ^ 10). http://wwwse.inf.tu-dresden.de/data/courses/SE1/SE1-2004-lec12.pdf , Seite 11. Dies gilt auch für Festplatten, CD-Laufwerke usw.

GUIDs sind statistisch eindeutig und die Daten, die Sie aus der Datenbank lesen, sind nur statistisch korrekt.


Sind Sie sicher, dass ich mein Netzwerk unmöglich rüsten konnte, sodass weniger als 1 von 10 ^ 28 Paketen beschädigt sind?
Joshua

13

Ich würde Occams Rasiermesser in diesem Fall als guten Leitfaden betrachten. Es ist unglaublich unwahrscheinlich, dass Sie eine GUID-Kollision haben. Es ist viel wahrscheinlicher, dass Sie einen Fehler haben oder dass jemand mit Ihren Daten herumspielt.


1
In dieser Situation ist Occams Rasiermesser überhaupt kein guter Leitfaden! Occams Rasiermesser sagt, dass der Fall mit den geringsten Annahmen höchstwahrscheinlich richtig ist. In dieser Situation ist der Fall einer GUID-Kollision tatsächlich viel einfacher, aber Occams Rasiermesser gilt nicht für eine Situation wie diese, in der wir bereits wissen, dass einer der Fälle unglaublich unwahrscheinlich ist.
Lockstock

11

Siehe den Artikel " Globally Unique Identifier" von Wikipedia . Es gibt verschiedene Möglichkeiten, GUIDs zu generieren. Anscheinend verwendete die alte (?) Methode die Mac-Adresse, einen Zeitstempel bis zu einer sehr kurzen Einheit und einen eindeutigen Zähler (um schnelle Generationen auf demselben Computer zu verwalten), sodass es nahezu unmöglich ist, sie zu duplizieren. Diese GUIDs wurden jedoch gelöscht, da sie zum Aufspüren von Benutzern verwendet werden konnten ...

Ich bin mir nicht sicher, welchen neuen Algorithmus Microsoft verwendet (der Artikel besagt, dass eine Folge von GUIDs vorhergesagt werden kann, dass sie keinen Zeitstempel mehr verwenden? Der oben verlinkte Microsoft-Artikel sagt etwas anderes ...).

Jetzt sind GUIDs sorgfältig so konzipiert, dass sie namentlich global einzigartig sind. Ich gehe also das Risiko ein, dass dies unmöglich ist oder mit sehr, sehr geringer Wahrscheinlichkeit. Ich würde woanders suchen.





9

Zwei Win95-Computer mit Ethernet-Karten mit doppelten MAC-Adressen geben unter streng kontrollierten Bedingungen doppelte GUIDS aus, insbesondere wenn beispielsweise die Stromversorgung im Gebäude unterbrochen wird und beide genau zur gleichen Zeit starten.


Ist es üblich, dass zwei verschiedene Computer dieselbe Ethernet-MAC-Adresse haben?
Dave Lucre

@ DaveLucre: Nein, aber es wurden Vorfälle aufgezeichnet.
Joshua

Ich bin wirklich neugierig, wie es dazu kommt. Ist es bei VMs wahrscheinlicher, dass für jede Netzwerkkarte zufällig ein MAC generiert wird? Ich habe noch nie von physischen Netzwerkkarten gehört, die mit doppelten MACs hergestellt werden! Art wirft einen massiven Schraubenschlüssel in die Werke, wenn das möglich ist!
Dave Lucre

Beeindruckend! Danke für den Link @Joshua! Was für ein kolossaler Mist!
Dave Lucre

@ DaveLucre Ich habe einige sehr billige USB-NICs verwendet, bei denen ALLE mit demselben MAC hergestellt werden. Aber das hat natürlich nichts mit der Mathematik der Zufälligkeit zu tun und alles mit der Faulheit des Herstellers.
Rudolfbyker

5

Ich werde dies mit "Ich bin keine Networking-Person, daher kann ich völlig inkohärente Sätze machen, die folgen" vorwegnehmen.

Als ich an der Illinois State University arbeitete, hatten wir zwei Dell-Desktops, die zu unterschiedlichen Zeiten bestellt wurden. Wir haben den ersten in das Netzwerk aufgenommen, aber als wir versuchten, den zweiten in das Netzwerk aufzunehmen, erhielten wir verrückte Fehler. Nach eingehender Fehlerbehebung wurde festgestellt, dass beide Computer dieselbe GUID produzierten (ich weiß nicht genau, wofür, aber beide waren im Netzwerk unbrauchbar). Dell ersetzte tatsächlich beide Maschinen als defekt.


3
Es war speziell die GUID. Dies hatte etwas mit der GUID zu tun, die von den Maschinen generiert wurde, als sie sich dem Netzwerk anschlossen. Es dauerte mehrere Wochen, bis Dell die Maschinen ausgetauscht hatte, da die GUIDs nicht identisch sein konnten. Wir konnten das Problem reproduzieren, Dell nahm die Maschinen zurück und konnte in ihren Netzwerken die gleichen Ergebnisse erzielen. Am Ende ersetzten sie beide Maschinen. Wie gesagt, ich bin keine Netzwerkperson, aber ich erinnere mich besonders daran, dass es ein Problem mit GUIDs war.
John Kraft

5

Ich weiß, dass Leute die Wohlfühlantwort mögen, dass GUIDs magisch und garantiert einzigartig sind, aber in Wirklichkeit sind die meisten GUIDs nur 121-Bit-Zufallszahlen (sieben der Bits werden für die Formatierung verschwendet). Wenn Sie sich mit einer großen Zufallszahl nicht wohl fühlen würden, sollten Sie sich mit einer GUID nicht wohl fühlen.


11
Empfehlen Sie auch, keine Netzwerke zu verwenden. Oder Computer. Paritätsbits können nur so viel!
Rushyo

Du hast das falsch verstanden. Es gibt zwei Dinge, die ich in diesem Beitrag sagen wollte: 1) Wenn Sie eine große Zufallszahl benötigen, verwenden Sie eine große Zufallszahl. Die Verwendung einer GUID als große Zufallszahl ist unnötig irreführend. (2)
Rick Yorgason

4
Was mir voll bewusst ist. Sie sagten: "Wenn Sie sich mit einer großen Zufallszahl nicht wohl fühlen würden." GUIDs sind jedoch so einzigartig, dass Sie feststellen werden, dass so ziemlich alles andere in einem Computer eher zufällig ist, selbst Operationen, die Sie für selbstverständlich halten. Es besteht eine größere Wahrscheinlichkeit, dass ein ausgeflippter Speicherfehler Ihre Identitätsspalte beschädigt, als dass eine (wahre) GUID-Kollision auftritt. Sie sollten sich bei ihnen nicht "unwohl" fühlen. Wenn sie für das Szenario nicht ideal sind, ist das in Ordnung - aber sie brauchen keine besondere Vorsicht.
Rushyo

3
Ich denke, das führt nirgendwo hin, aber die Leute versuchen Ihnen zu erklären, dass Fehlererkennungsmechanismen in gängiger Hardware wie Netzwerkkarten oder Festplatten Algorithmen verwenden, bei denen die Wahrscheinlichkeit größer ist, dass sie keinen Fehler erkennen als bei einer GUID-Kollision Wenn Sie sich auf diese verlassen, können Sie sich auch auf GUIDs verlassen
Guillaume86

1
@ Rick, hängt davon ab, wie groß deine Nummer ist. Auf keinen Fall mit einem 4-Byte-Int oder 8-Byte-Bigint. GUID = 16 Bytes, daher benötigen Sie eine benutzerdefinierte 16-Byte-Implementierung mit großen Zahlen, um die gleichen 2 ^ 128 möglichen Kombinationen zu erzielen. So allgemein gesprochen, wenn Zahlen mit ‚normal‘ int oder bigint zufällig, die Möglichkeit von Kollisionen mit einer GUID ist niedriger (Weglassen Zufall algo Überlegungen für jeden).
Wim Hollebrandse

3

Könnte der Code, der zum Generieren einer GUID verwendet wird, einen Fehler enthalten? Ja, natürlich könnte es. Die Antwort ist jedoch die gleiche wie bei einem Compiler-Fehler. Ihr eigener Code ist um Größenordnungen wahrscheinlicher fehlerhaft. Schauen Sie also zuerst dort nach.


2

Natürlich ist es möglich ... wahrscheinlich? Nicht wahrscheinlich, aber es ist möglich.

Denken Sie daran, dass dieselbe Maschine jede GUID (den Server) generiert, sodass ein Großteil der "Zufälligkeit", die auf maschinenspezifischen Informationen basiert, verloren geht.


1

Versuchen Sie nur zum Grinsen das folgende Skript ... (funktioniert unter SQL 2005, nicht sicher über 2000)

declare @table table
(
    column1 uniqueidentifier default (newid()),
    column2 int,
    column3 datetime default (getdate())
)

declare @counter int

set @counter = 1

while @counter <= 10000
begin
    insert into @table (column2) values (@counter)
    set @counter = @counter + 1
end

select * from @table

select * from @table t1 join @table t2 on t1.column1 = t2.column1 and t1.column2 != t2.column2

Wenn Sie dies wiederholt ausführen (dauert weniger als eine Sekunde), wird von der ersten Auswahl an ein ziemlich großer Bereich erzeugt, selbst bei einer EXTREM kurzen Zeitlücke. Bisher hat die zweite Auswahl nichts produziert.


1
Sie benötigen weitere 15 Nullen am Ende des Zählers, um eine 50% ige Chance auf ein Duplikat zu haben. Aber um Himmels willen, tu es nicht!
Jim Birchall

0

Unmöglich, wenn die Benutzer unterschiedliche Computer mit Netzwerkkarten haben, und selbst wenn nicht, ist dies immer noch ein äußerst geringes, fast theoretisches Risiko.

Persönlich würde ich woanders hinschauen, da es eher ein Fehler als ein GUID-Konflikt ist ...

Vorausgesetzt natürlich, Sie hacken die GUID nicht ab, um sie zu verkürzen.


Die GUIDs würden auf dem Server generiert, sodass die Netzwerkkarten des Benutzers nicht ins Spiel kommen würden.
Tom Ritter

0

Sicher ist es möglich und vielleicht sogar wahrscheinlich. Es ist nicht so, dass sich jede GUID in einem zufälligen Teil des möglichen Zahlenraums befindet. Für den Fall, dass zwei Threads gleichzeitig versuchen, einen zu generieren, könnten sie, abgesehen von einer Art zentraler GUID-Funktion mit einem Semaphor, denselben Wert erhalten.


0

Es ist sehr unwahrscheinlich, dass Sie auf GUID-Kollisionen stoßen, wenn Sie diese über die NEWID()Funktion in SQL Server generieren (obwohl dies natürlich möglich ist, wie andere Antworten hervorgehoben haben). Eine Sache, auf die sie nicht hingewiesen haben, ist, dass es tatsächlich sehr wahrscheinlich ist, dass Sie auf Kollisionen stoßen, wenn Sie GUIDs in JavaScript in Browsern in freier Wildbahn generieren. Es gibt nicht nur manchmal Probleme im RNG in verschiedenen Browsern, sondern ich bin auch auf Probleme gestoßen, bei denen die Google-Spider die Ergebnisse solcher Funktionen zwischenzuspeichern scheinen und wiederholt dieselbe GUID an unsere Systeme übergeben haben.

Weitere Informationen finden Sie in den verschiedenen Antworten hier:

Kollisionen beim Generieren von UUIDs in JavaScript?

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.