POST is the "append" verb, and also the "processing" verb. PUT is the "create/update" verb (for known identifiers), and almost looks like the right choice here, because the full target URI is known. projectId and siteId already exist, so you don't need to "POST to a collection" to produce a new ID.
The problem with PUT is that it requires the body be the representation of the resource you're PUTting. But the intent here is to append to the "project/sites" collection resource and, rather than updating the Site resource.
What if someone PUTs a full JSON representation of an existing Site? Should you update the collection and update the object? You could support that, but it sounds like that's not the intent. As you said,
the only data I really need are ProjectId and SiteId
Rather, I'd try POSTing the siteId to the collection, and rely on the "append" and "process" nature of POST:
POST myapi/projects/{projectId}/sites
{'id': '...' }
Since you're modifying the sites collection resource and not the Site resource, that's the URI you want. POST can know to "append/process" and add the element with that id to the project's sites collection.
That still leaves the door open to creating brand new sites for the project by fleshing out the JSON and omitting the id. "No id" == "create from scratch". But if the collection URI gets an id and nothing else, it's pretty clear what needs to happen.
Interesting question. :)