It appears that you've put Trigger.new into the Contacts variable. This is problematic, because relationship fields are not populated by default, and as it so happens, Boolean values default to a false value if you try to reference them without querying them. You can easily verify this behavior with this simple Execute Anonymous Script:
Contact c = new ...
Answering to add to KB of these often inscrutable errors.
There could be several reasons but in our org, here was the issue:
Account and Contact: Private
Order: Controlled by parent
Account sharing rule:
RecordType Foo: Shared Read/Write with Group A
RecordType Bar: Shared Read/Write with Group B
Running User: Alice
There's two types of controls you're asking about: sharing and profile permissions. The read-write restriction on master-detail relationships only cares about sharing, not profile permissions. If sharing says a user can edit a record (even if the profile restricts it), then they can create child records. This is the intended behavior of master-detail ...