The assembler has a list of csrs, because it needs to be able to assemble them. In the binutils source tree, see the DECLARE_CSR macros in the include/opcode/riscv-opc.h file. Note that this is an internal interface, and is subject to change in future binutils versions.
The github.com/riscv/riscv-opcodes module has info on csrs. The parse-opcodes file is a python script that has a list of csrs with names and addresses. This is meant to be used to generate header files and is part of the riscv-tools repo. There might be a usable list of csrs somewhere in riscv-tools.
As the previous poster mentioned, the privilege spec is the best place to look, and most SOCs only implement a subset of the CSRs. Also, the list of CSRs changes occasionally, as the spec is still evolving. Some proposed extensions will add new CSRs for instance. Different architecture versions have different csr lists, and csr addresses have changed in the past, but hopefully not anymore.