A simple way to determine mesh orientation is to compute the winding order of each triangle. Where winding order is either clockwise (CW) or counter clockwise (CCW).
Swapping the winding order of a triangle swaps the direction of the normal for that triangle. So checking that normal vectors are facing "outward" is a matter of verifying that all triangles are wound in the same direction.
Keep in mind that the coordinate system also plays a role in handedness. Determining that the mesh is wound consistently is a better indicator of a valid mesh since sometimes a mesh is intentionally wound in the opposite direction so that we only see the "inside" of the faces.
I well specified system will have all these details in it so that the facing can be determined to be consistent with that system. This way we aren't throwing a random mesh at a random system to do coin flips on the problem.
There are many ways to go about checking this.
This is more of a quick check but is fairly effective in practice.
Given a mesh with averaged normals, for each triangle compute the faceted normal and compute the cos of the angle between the averaged normal and the faceted normal using the dot product. A positive result indicates clockwise, a negative result indicates counter clockwise, and zero indicates that the two normals are very close to each other. Keeping a count of clockwise and counter clockwise where one of the two should be zero depending on the winding order of the mesh.
If all the triangles have the same winding order then the mesh can be accepted as being consistent and "outward" is defined as all faces having either clock wise or counter clock wise orientation depending on the handedness of the mesh.
The rational behind this method is that most normal vectors are computed as averaged values. So if you have a mesh that is largely valid then the averaged normal will very likely face the correct direction.
Another way to check winding without doing any computations is to check that the edge of any connected triangles have the vertices ordered in the opposite direction. If any of the edges have the same order the connected triangle has opposite winding and can be fixed by swapping the order of the vertices. This method is very reliable and very easy to implement. For example if we have triangle ABC where AB, BC, and CA are the edges then the connected triangles should have edges BA, BC, and CA.
The property "connected triangles with the same winding have edges with the opposite order" makes this particularly easy to implement since this means that if edge AB is repeated anywhere then there is a problem. IE AB is unique in the mesh. Implementation is just a matter of picking your favorite data structure and using insert unique. That's it we're done. Non-unique means there is a problem, reverse the winding of that triangle and continue. Done.
More Rigorous solutions
Some interesting information on the subject can be found by searching for the shoelace formula and greene's algorithm. Also search for shoelace in 3D. Search for curve orientation (here is a wikipidia page on it) can be useful too.
A much more rigorous solution can be created by generating edge information... pick a single triangle determine its orientation, then use its edges to find and check the orientation of neighboring triangles. Save orientation with the edges and compare new triangles against that orientation. Repeat until all triangles are traversed.